Практика XDTO. Нюансы

Некоторые практические моменты работы с XDTO. Дополнения к очевидному.

Основой работы является принцип создания экземпляров данных на основании метаданных, т.е. так же, как в "обычной" 1С. Метаданными выступают XDTO-пакеты, в которых прописаны типы и свойства объектов, допустимые значения, форматы значений, словом — та же конфигурация (недаром и саму конфигурацию можно выгрузить в xsd, а по утверждениям некоторых, 1С так внутри себя и работает). Экземплярами выступают конкретные XDTO-объекты и значения. Созданием и управлением всем этим занимаются фабрики XDTO, а если речь идёт о встроенных объектах 1С, то ещё сериализаторы. И об объектах, и о свойствах, и о фабриках есть множество хороших статей, поэтому баянить не буду, а обозначу некоторые тонкости, которые недостаточно освещены. Поскольку в чести последнее время практика, то ею и займёмся, а теория — в другой раз.

Для работы нужны: пространство имён, в пределах которого уникальны имена метаданных, и фабрика, в пакеты которой входят все нужные описания всех типов, их форматов и допустимых значений.

Фабрики или создают "по месту", или используют синглтон ФабрикаXDTO. Основное их различие при этом — наборы пакетов, которые у этих фабрик будут в доступности.

Пространства имён обычно получают так:

УРИ=Метаданные.ПакетыXDTO.<ИмяВашегоПакета>.ПространствоИмен; // если он встроен в метаданные
// или
УРИ=ФабрикаXDTO.Пакеты.Получить(УРИилиНомерВашегоПакета).URIПространстваИмен; // в общем случае

Создание экземпляра по метаданным это, как мы знаем:

хдтоОбъект=ФабрикаXDTO.Создать(хдтоТип);

Но откуда берётся сам тип? В простейшем случае он описан и получается по имени и пространству имён, например:

 

 

Тип напрямую чётко указан и так же просто берётся:

типКонтрагент=ФабрикаXDTO.Тип(УРИ,"Контрагент");

Возможна следующая ситуация, когда тип не самостоятелен, а описан в свойстве:

 

 

В этом случае тип следует получать из свойства исходного типа, а если у этого свойства есть своё определение, то разыменовывая из него:

типСтавкиНалогов=ФабрикаXDTO.Тип(УРИ,"Товар").Свойства.Получить("СтавкиНалогов").Тип;
типСтавкаНалога=ТипСтавкиНалогов.Свойства.Получить("СтавкаНалога").Тип;

Типизация некоего свойства может указывать на корневое:

 

 

Аналогично, тип для создания экземпляра получаем из свойства:

типПакет=ФабрикаXDTO.Пакеты.Получить(<УРИилиНомерВашегоПакета>).КорневыеСвойства.Получить("ПакетПредложений").Тип;

также, если значение есть, можно вытащить из него:

типХДТО=ОбъектИлиЗначение.Тип;

Ещё, работая с дочерними полями, удобно использовать метод ВладеющееСвойство().

 

И вот, мы можем создать наш объект: хдтоОбъект=ФабрикаXDTO.Создать(типОбъекта).

Свойствам (реквизитам, так сказать) этого объекта присваиваются значения. Каковы они, мы можем узнать из изучения пакета, объявленных там типов, их фасетов и ограничений (желающим ненавязчиво советую мою обработку-браузер XDTO). Если идут ссылки на другие пакеты, то проверьте, чтоб они были в вашей фабрике. Также следите, чтобы пространства имён были бы объявлены чётко и явно, см. чуть ниже).

С присвоением свойств есть несколько тонкостей.

Во-первых, вроде бы подходящее значение "не лезет" — или ругается при присвоении, или при сериализации в строку бредово выглядит, или ресурс, которому такой хмл пихают, обижается. Помогает предельно скрупулёзное следование типизации:

// вместо
хдтоТовар.Артикул=СокрЛП(Артикул);
// если объявлен тип, то надо так:
хдтоТовар.Артикул=ФабрикаXDTO.Создать(ФабрикаXDTO.Тип(УРИ,"АртикулТип"),СокрЛП(Артикул));

Во-вторых, реквизиты, имеющие списочный тип (список XDTO может начинаться с 0, с 1, с любой мин.границы, но всегда верхняя будет -1) — так вот списочные иногда требуют создания промежуточной переменной-списка:

// простой случай
хдтоОбъект.СписочноеСвойство.Добавить(хдтоЭлементСписка);
// через объект-коллекцию
хдтоСписок=ФабрикаXDTO.Создать(типСписка);
хдтоСписок.СписочноеСвойство.Добавить(хдтоЭлементСписка);
хдтоОбъект.СписочноеСвойство=хдтоСписок;

и иногда это единственно верный вариант.

В третьих, наивно ожидать от просмотра в отладчике, что вам откроется истина. В огромном количестве случаев значения реквизитов объекта будут пусты, а их тип "Неопределено", и вы ничего не поймёте. Поэтому — смотрите объявления в пакете, смотрите свойства. Наиболее важны для объектов — базовый тип, упорядоченность, смешанность; для свойств — тип и возможность пустого. Не верьте отладчику.

 

Некоторые сетуют, что-де формат XDTO есть данность и её не расширить. Это не совсем так, и этим пакеты отличаются от метаданных 1С. Если тип объекта имеет свойство "Открытый" = Истина, то можно прямо на лету добавлять всё, что заблагорассудится, в рамках допустимых типов, конечно:

знч=ФабрикаXDTO.Создать(ФабрикаXDTO.Тип(УРИ,"СтрокаТип"),"Добавляю, как хочу");
хдтоОбъект.Добавить(ФормаXML.Элемент,УРИ,"МоеДопПоле",знч);
//
// и будет это <МоеДопПоле>Добавляю, как хочу</МоеДопПоле>

Правда, вы потом это доп.поле не удалите из экземпляра, а только сможете сбросить или очистить. Но для разных экземпляров одного типа вы сможете понадобавлять, что захотите. Правда, читающему в рамках ожидаемой xsd такая самодеятельность добавит проблем.

 

Когда объект создан, его можно записать в реквизит некоего "старшего" объекта, или добавить в коллекцию однотипных собратьев. Можно и просто засунуть в нужное место хмл-текста. Прямо целиком. Например:

// в этом примере УРИ взято из CML 2.Х
Запись=Новый ЗаписьXML;
Запись.УстановитьСтроку("UTF-8");
Запись.ЗаписатьОбъявлениеXML();
Запись.ЗаписатьНачалоЭлемента("КоммерческаяИнформация");
ФабрикаXDTO.ЗаписатьXML(Запись,хдтоКонтрагент,,УРИ,ФормаXML.Элемент,НазначениеТипаXML.Неявное);

Тут есть тонкость, связанная с пространством имён и локальными именами. Третьим параметром рекомендую указывать локальное имя, если оно есть или возможен конфликт с контекстом, куда пихается хдто. Для экземпляров "старших" типов оно совпадает с именем типа, для остальных можно получать его через ВладеющееСвойство() или Свойство.

А вот с пространством имён сложнее. У 1С всегда есть пространство по умолчанию, и пишется оно в xmlns без префикса. И вот если, впихивая хдто в ЗаписьХМЛ, вы его не укажете, то оно будет дописано в тег сериализованного экземпляра, т.е. в нашем случае <Контрагент xmlns="urn:1C.ru:commerceml_2">. Если тегу, в который вносится хдто, указать пространство имён в виде 

Запись.ЗаписатьНачалоЭлемента("КоммерческаяИнформация", УРИ);

то 1С сгенерирует явный префикс и всё вложенное пустит с уточнением, какое это пространство имён:

<d1p1:КоммерческаяИнформация xmlns:d1p1="urn:1C.ru:commerceml_2">
и все вложенные без конкретизации будут идти с ним же: <d1p1:Контрагент>

Поэтому рекомендую и при обычной, и при смешанной записи хдто в хмл явно указывать сопоставления, например:

Запись.ЗаписатьСоответствиеПространстваИмен("","urn:1C.ru:commerceml_2"); // так делается УРИ по умолчанию
Запись.ЗаписатьСоответствиеПространстваИмен("xsi","http://www.w3.org/2001/XMLSchema-instance");

ну и так далее.

Кстати, превращение хдто в хмл, а его в текст, и затем ЗаписатьБезОбработки — тоже полезная штука, но тут примеров и без меня полно.

Читать экземпляры, между прочим, тоже можно с произвольного места хмл-текста, чередуя с обычным чтением хмл:

Пока Чтение.Прочитать() Цикл
Если Чтение.ЛокальноеИмя="НужноеНам" Тогда // например, так нашли нужное
хдтоОбъект=ФабрикаXDTO.ПрочитатьXML(Чтение,типОбъекта); Прервать
КонецЕсли;
КонецЦикла;

 

Собственно, всё. Ну и советую не забывать про хдтоОбъект.Проверить(), иногда реально ловит невалидные и незаметные ситуации.

Советую всем статьи Evil Beaver’a, ибо они полны, точны и актуальны, и вообще труЪ.

24 Comments

  1. Yashazz

    Коллеги, у меня одного картинка задвоилась, или это реально так?

    Reply
  2. AlX0id

    (1)

    у меня норм

    Reply
  3. Yashazz

    (2) и вот такой ерунды, как на скриншоте, не наблюдается?

    Reply
  4. user774630

    (3) не наблюдается

    Reply
  5. ВикторП

    Еще есть вариант отображения

    Reply
  6. Sashares

    (5)У меня также

    Reply
  7. AlX0id

    (3)

    Не — все прям норм )

    Reply
  8. Yashazz

    Короче руки у меня кривые, или браузер… Печаль… Спасибо, коллеги, а то даже проверить было не на чем.

    Reply
  9. Yashazz

    Поправка: в строке кода

    типПакет=ФабрикаXDTO.Пакеты.Получить().КорневыеСвойства.Получить(«ПакетПредложений»).Тип;

    естественно Пакеты.Получить(<НомерПакетаИлиУРИ>)

    Reply
  10. triviumfan

    Доступно написано, спасибо.

    Reply
  11. Trotter_NN

    как хранить xsd схему в общих макетах ? для мобильного приложения

    Reply
  12. Yashazz

    (11) Можно как файл, можно как текстовый макет; без разницы. Она по сути есть плоский текст, не более. А дальше прочитать из макета, загнать в DOM, оттуда в построитель схем ХМЛ, метод СоздатьСхемуXML и всё.

    Reply
  13. Trotter_NN

    (12)

    На моей картинке только несколько вариантов, можно пример с двоичными данными ?

    Reply
  14. Yashazz

    (13) Да без разницы, двоичные или нет.

    имяф=ПолучитьИмяВременногоФайла(«xsd»);
    рМакет=ПолучитьМакет(«МакетСоСхемой»);
    рЧтение=Новый ЧтениеXML;
    Если ТипЗнч(рМакет)=Тип(«ДвоичныеДанные») Тогда
    рМакет.Записать(имяф);
    рЧтение.ОткрытьФайл(имяф);
    ИначеЕсли ТипЗнч(рМакет)=Тип(«ТекстовыйДокумент») Тогда
    рЧтение.УстановитьСтроку(рМакет.ПолучитьТекст());
    КонецЕсли;
    //
    пострДОМ=Новый ПостроительDOM;
    докДОМ=пострДОМ.Прочитать(рЧтение);
    //
    пострСхем=Новый ПостроительСхемXML;
    рСхема=пострСхем.СоздатьСхемуXML(докДОМ.ЭлементДокумента);
    Если рСхема<>Неопределено Тогда
    // делаем, что надо
    КонецЕсли;
    

    Показать

    Reply
  15. ids79

    Спасибо за статью.

    У меня такой вопрос, можно ли каким-то образом дополнить стандартную фабрику XTDO дополнительным пакетом «на лету»? Например, загрузить дополнительную схему xsd из макета.

    На сколько я знаю, нет. Но может быть я ошибаюсь…

    Reply
  16. Yashazz

    (15) Первое, что приходит в голову, это вывалить схему в файл, тупо его покрыжить как текст, вставив в нужное место нужный блок, и прочитать обратно. Но это всё равно не «стандартная фабрика», а новую фабрику делать придётся на основании этой дополненной схемы.

    Reply
  17. genayo

    Следует отметить, что в реализации XDTO есть проблемы, некоторые валидные XSD-схемы оно не понимает, и в этом случае понять, что не так, достаточно сложно. Например, обсуждение здесь https://forum.mista.ru/topic.php?id=840840

    Reply
  18. ids79

    (16) С новой то фабрикой все понятно. Я имел в виду именно основную. Но похоже нет такой возможности

    Reply
  19. PLAstic

    Не соглашусь насчёт «полноты» и «тру». Если бы кто-то занимался плотно хдто, он давно бы озвучил в своих статьях, что у платформы 1С есть большие проблемы.

    * Определяем тип объекта, реквизит и значение по умолчанию для него. Создаём объект. Видим в окне отладки, что реквизиту присвоено дефолтное значение. Сериализуем в json — реквизита нет (в xml не проверял, но думаю, так же). Нет его потому, что значение по умолчанию просто показывается нам в окне отладки, но на самом деле не присваивается реквизиту полноценно. Ошибка зарегана в 1С для рассмотрения. Не знаю уж, примут или нет.

    * Создаём пустой список xdto, сериализуем в json. Списка нет. Вообще.

    * Окей, может это проблема сериализации? Определяем пустой массив в соответствии с RFC8259: «array»:[] Пытаемся его десериализовать в объект с реквизитом-списком: ошибка. Говорит, странное окончание тега в районе «]».

    * Сейчас точно не вспомню, но есть сложности с сериализацией/десериализацией «null».

    Открытые типы, как верно было сказано, ставят крест на десериализации. У вас описана схема, вы по ней работаете год и тут бац — механизм накрылся. Смотрим — новое св-во зашло в объект. Или вдруг целое значение стало дробным. По хорошему, поставщикам API нужно версионировать схемы и предоставлять возможность подписки на эти изменения, чтобы пользователи схемы имели время перестроить свои системы на новую версию к сроку её включения. Но это уже не проблемы платформы…

    Reply
  20. Yashazz

    (17) О дааа. Сколько я мучился, пытаясь в своё время скормить схему, валидную с точки зрения других приложений, 1С — это была песня. Экспериментально выявил ряд мест, которые 1С не нравятся, но иногда из-за них надо по смыслу курочить схему, поэтому упс.

    Согласен.

    Reply
  21. Yashazz

    (18) Похоже, нет. Это всё равно что новый объект в метаданные «на лету» добавлять. Может, сборка/разборка в файлы и даст эффект, но всё равно костыль, а других вариантов не вижу.

    Reply
  22. Yashazz

    (19) Согласен.

    1. Да, есть такой эффект, потому и написал, что отладчику веры нет.

    2. В хмл одно время была та же фигня, не проверял, есть ли такое в наши дни.

    3. А вот на скобку у меня не жаловался. Какие были свойства у этого списка в хдто-пакете? Было разрешено, что он с 0 элементов, пустой?

    4. Был какой-то релиз 8.3.8, который литерал null чуть ли не как директиву воспринимал. Других эффектов не помню.

    Reply
  23. PLAstic
    Reply
  24. Yashazz

    (23) ну красота, что тут скажешь… спасибо за информацию.

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *