Практика 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 не будет опубликован. Обязательные поля помечены *