Перенос данных с использованием XDTO

Использование объекта типа «ОбъектXDTO» для переноса данных из одной конфигурации в другую

Здравствуйте! Моя статья посвящена моему вниканию в XDTO, первые шаги. Статьи по XDTO есть, но там слишком много теории.

Предисловие

Мне захотелось попробовать использовать XDTO для написания своего переноса данных из одной конфигурации в другую. У меня была конкретная цель: создать управляемый перенос данных из одной конфигурации в другую. Из дописанной УТ10 в БУХ30. Естественно, в УТ10 нет БСП. У нас использовалась конвертация данных 2, я умею ей пользоваться, в некотором смысле, и даже вижу косяки в ее справке. Но гибкость переноса маловата. Данные перенести можно, но мне не нравилась малая скорость, сложность создания/изменения своего алгоритма загрузки. Например, некоторые документы мне нужно принимать, другие — нет, по каким-то условия, некоторые проводить, а некоторые — не трогать совсем, необходимость использования разных правил обмена для разных конфигураций, сложность просмотра данных при отладке и т.д. Но я в работе всегда стараюсь пробовать новые механизмы, чтобы осознавать, чем хорош/плох (хорошо, когда соотношение 50/50, люблю хорошую ортогональность) тот или иной подход. Скорее всего то понимание, которое я хочу показать, возможно завтра уже вызовет у меня только улыбку. Но я думаю стоит поделиться, уверен, кому-то поможет.

Итак, начинаю описывать то, что я понял…

Для начала, необходимо представить три понятия…

  1. …Что такое пакет XDTO. Пакет XDTO — это описание того, как нужно строить XML. Какие там узлы вложены, сколько их и как интерпретировать содержимое узлов. Интерпретация узлов зависит от…
  2. …Пространства имен. Это некое абстрактное название совокупности описаний этих интерпретаций. Видя в узле атрибут xmlns:v8="http://v8.1c.ru/8.1/data/enterprise/current-config" мы понимаем, что в подчиненных узлах этого узла данные типа <ДокументОснование xsi:type="v8:DocumentRef.Возврат">fdfddc9e-6357-11e9-9869-c86000e2d5b1</ДокументОснование> означают, что тип "DocumentRef.Возврат" относится к v8, а v8 — это префикс пространства имен, которое мы объявили для этого узла (или вышестоящего). В этом примере получается так: содержимое "fdfddc9e-6357-11e9-9869-c86000e2d5b1" узла <ДокументОснование> следует интерпретировать согласно типу "DocumentRef.Возврат", описанному в пакете с пространством имен "http://v8.1c.ru/8.1/data/enterprise/current-config". Создание объекту описанных типов согласно пространству имен занимается…
  3. …ФабрикаXDTO. ФабрикаXDTO знает типы объектов по своим пакетам и умеет создавать ОбъектXDTO по типам, описанным в них. Есть глобальная фабрика. Она знает обо всех типах данной конфигурации. Но можно создавать свою фабрику по своим схемам.

Моя задача по-большей части заключалась в том, чтобы логичным образом перенести ссылки на документы. Ссылка в одной конфигурации должна соответствовать ссылке в другой и чтобы это сразу было видно в отладчике, в свойстве ОбъектXDTO. Но вот незадача: в одной конфигурации документ называется, например, "ВозвратОтПокупателя", а в другой — "Возврат". А ссылки должны совпадать. Незадача это потому что пространство имен конфигурации в обоих базах называется одинаково: "http://v8.1c.ru/8.1/data/enterprise/current-config". Вот только в каждой из них свои объекты. Или, например, названия перечислений или значения перечислений разные.

Можно, конечно, всё создавать вручную через ЗаписьXML, все атрибуты и узлы, согласно конфигурации базы-приемника… Но! Мне далеко не всегда нравится состав реквизитов этих объектов. Например, для чего мне в формируемом XML по документу платежки данные счетов учета? Или мне не хотелось создавать в передаваемом XML данные для РасшифровкаПлатежа. Мне хотелось сделать свой ОбъектXDTO, со своими свойствами, которые сочту необходимыми для переноса. 

Решение

Создаем ПакетXDTO в базе-приемнике (это у меня БУХ30, я добавил расширение, но возможно это можно создавать и в блокноте или еще каком-то инструменте) и экспортируем его в файл .xsd. Затем экспортируем всю конфигурацию базы приемника в другой файл. 

В базе-источнике для создания своей фабрики из двух файлов .xsd пишем вот что:

Функция ПолучитьСхемуXML(ИмяФайла)
Файл = Новый Файл(ИмяФайла);
ЧтениеXML = Новый ЧтениеXML;
// Открыть файл XML
ЧтениеXML.ОткрытьФайл(Файл.ПолноеИмя);
// Создать построитель документа DOM по умолчанию
ПостроительDOM = Новый ПостроительDOM;
// Прочитать файл XML в документ DOM
ДокументDOM = ПостроительDOM.Прочитать(ЧтениеXML);
// Создать построитель схемы XML по умолчанию
ПостроительСхемыXML = Новый ПостроительСхемXML;
// Получить схему XML из документа DOM
СхемаXML = ПостроительСхемыXML.СоздатьСхемуXML(ДокументDOM);
Возврат СхемаXML;
КонецФункции



НаборСхемXML = Новый НаборСхемXML;
Файл = Новый Файл("D:XDTOexport-2.xsd");
НаборСхемXML.Добавить(ПолучитьСхемуXML(Файл.ПолноеИмя));
Файл = Новый Файл("D:XDTOexport.xsd");
НаборСхемXML.Добавить(ПолучитьСхемуXML(Файл.ПолноеИмя));
МояФабрика = Новый ФабрикаXDTO(НаборСхемXML);

Таким образом у нас получается Фабрика, которая может создавать объекты с необходимыми типами.

Пакет в базе-приемнике выглядит так: 

 

Это то, как я хочу переносить данные. Этот мой пакет имеет пространство имен с именем (http://www.sample-package.org). Мне в документе ОплатаПлатКартой не нужны счета и все реквизиты контрагента. Мне у контрагента нужно наименование и возможно ссылка. Тип у свойства Ref типа объекта Контрагента — строка. Потому что я не собираюсь сопоставлять контрагентов по ссылке. А вот тип свойства Ref у типа объекта ОплатаПлатКартой — DocumentRef.ОплатаПлатежнойКартой из пространства имен конфигурации http://v8.1c.ru/8.1/data/enterprise/current-config. Именно поэтому нам нужна директива импорта и это то, ради чего я это делал: просмотр в отладчике этого свойства у ОбъектXDTO выдаст реальную ссылку в базе-приемнике.

ВидОперации тоже имеет тип из типов конфигурации.

А вот ДокументОснование не имеет типа, он имеет открытый тип. В базе приемнике у соответствующего документа составной тип, поэтому при формировании XML необходимо передавать тип.

Вот как выглядит функция формирования XML:

 ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.УстановитьСтроку();
ЗаписьXML.ЗаписатьОбъявлениеXML();
ЗаписьXML.ЗаписатьНачалоЭлемента("root");
ЗаписьXML.ЗаписатьСоответствиеПространстваИмен("", "http://www.sample-package.org");
ЗаписьXML.ЗаписатьСоответствиеПространстваИмен("v8", "http://v8.1c.ru/8.1/data/enterprise/current-config");

МассивДокументов = ПолучитьСписокДокументов();

Для Каждого ТекЭлемент Из МассивДокументов Цикл
Объект = ТекЭлемент.ПолучитьОбъект();
ТипРеализация = МояФабрика.Тип("http://v8.1c.ru/8.1/data/enterprise/current-config", "DocumentRef.Реализация");
ТипДокумента = МояФабрика.Тип("http://www.sample-package.org", "ОплатаПлатКартой");
ТипКонтрагента = МояФабрика.Тип("http://www.sample-package.org", "Контрагент");
ТипВозврата = МояФабрика.Тип("http://v8.1c.ru/8.1/data/enterprise/current-config", "DocumentRef.Возврат");
ТипОплата = МояФабрика.Тип("http://v8.1c.ru/8.1/data/enterprise/current-config", "EnumRef.ВидыОперацийПлатКарта");

КонтрагентXDTO = МояФабрика.Создать(ТипКонтрагента);
КонтрагентXDTO.Ref = XMLСтрока(Объект.Контрагент);
КонтрагентXDTO.Description = "" + Объект.Контрагент;

ОбъектXDTO = МояФабрика.Создать(ТипДокумента);
ОбъектXDTO.Ref = XMLСтрока(Объект.Ссылка);
ОбъектXDTO.Description = "" + Объект;
ОбъектXDTO.СуммаДокумента = Объект.СуммаДокумента;
ОбъектXDTO.ВидОперации = МояФабрика.Создать(ТипОплата, "ОплатаПокупателя");
ОбъектXDTO.Контрагент = КонтрагентXDTO;
ОбъектXDTO.ДокументОснование = МояФабрика.Создать(ТипВозврата, Объект.ДокументОснование.УникальныйИдентификатор());

МояФабрика.ЗаписатьXML(ЗаписьXML, ОбъектXDTO);
КонецЦикла;

ЗаписьXML.ЗаписатьКонецЭлемента();

Результат = ЗаписьXML.Закрыть();

Функции списка документов не привожу — это просто ссылки на документы оплаты.

Объекты XDTO создаются фабрикой по типу полученному из фабрики: сначала получаем тип, потом создаем ОбъектXDTO (или ЗначениеXDTO, как в случае, например, с перечислением).

Обратите внимание, как формируется свойство Ref для Контрагента — это просто строка, полученная из ссылки.

Обратите внимание на заполнение свойства ДокументОснование. Мы создаем фабрикой значение из ТипВозврата, передавая ей уникальный идентификатор ссылки основания. У документа основания в базе-источнике составной тип: РеализацияТоваровУслуг и ВозвратОтПокупателя. А вот в базе-приемнике они называются иначе: Реализация и Возврат. 

Наименования значений перечислений в базе источнике и в базе приемнике тоже разные: Оплата, Возврат и ОплатаПокупателя, ВозвратПокупателю. В приведенном примере заполняется однозначно, но, думаю, понятно, что в реальном обмене необходимо добавлять условия, как именно заполнять создаваемый ОбъектXDTO.

Итого получается XML:

<?xml version="1.0"?>
<root xmlns="http://www.sample-package.org" xmlns:v8="http://v8.1c.ru/8.1/data/enterprise/current-config">
<ОплатаПлатКартой xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Ref>fdfddc9f-6357-11e9-9869-c86000e2d5b1</Ref>
<Description>Оплата платежной картой 000000001 от 20.04.2024 15:35:46</Description>
<ВидОперации>ОплатаПокупателя</ВидОперации>
<Контрагент>
<Ref>fdfddc9b-6357-11e9-9869-c86000e2d5b1</Ref>
<Description>Виктор</Description>
</Контрагент>
<СуммаДокумента>1000</СуммаДокумента>
<ДокументОснование xsi:type="v8:DocumentRef.Возврат">fdfddc9c-6357-11e9-9869-c86000e2d5b1</ДокументОснование>
</ОплатаПлатКартой>
<ОплатаПлатКартой xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Ref>fdfddca0-6357-11e9-9869-c86000e2d5b1</Ref>
<Description>Оплата платежной картой 000000002 от 20.04.2024 15:35:57</Description>
<ВидОперации>ОплатаПокупателя</ВидОперации>
<Контрагент>
<Ref>fdfddc9b-6357-11e9-9869-c86000e2d5b1</Ref>
<Description>Виктор</Description>
</Контрагент>
<СуммаДокумента>300</СуммаДокумента>
<ДокументОснование xsi:type="v8:DocumentRef.Возврат">fdfddc9e-6357-11e9-9869-c86000e2d5b1</ДокументОснование>
</ОплатаПлатКартой>
</root>

Для чтения такого XML в базе приемнике пишем короткую функцию:

 ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.УстановитьСтроку(ДанныеXML);
ЧтениеXML.Прочитать();
ЧтениеXML.Прочитать();
ТипXML = ПолучитьXMLТип(ЧтениеXML);
ТипXDTO = ФабрикаXDTO.Тип(ТипXML);
ОбъектXDTO = ФабрикаXDTO.ПрочитатьXML(ЧтениеXML, ТипXDTO);

ЧтениеXML.Прочитать() — это условная функция, чтобы выйти на нужный узел, добавьте циклов сами, как вам нужно. По узлу определяем ТипXML, находим его в ФабрикаXDTO (здесь фабрика — глобальная, потому что у меня прямо в конфигурации есть нужный пакет XDTO), получая ТипXDTO. По этому типу XDTO читаем объект.

После этого кода в ОбъектXDTO будет следующее:

Сравните с тем, что было в исходной базе:

Необходимые мне ссылки преобразовались, как мне было нужно.

В прилагаемом файле архив с базами, где в отладчике можно посмотреть, как это работает.

Буду рад критике и предложениям!

10 Comments

  1. HAMMER_59

    Все подумываю написать статью «XDTO для чайников», а тут смотрю может уже и не нужно.

    Сейчас также пишу синхронизацию между доработанной УТ 10.1 и БП 3, рекомендую посмотреть стандартные правила обмена УТ 10.3 -> БП 3.

    По статье отметил бы:

    — Непонятно зачем программно делать экспорт схемы, когда можно через графический интерфейс.

    — У меня самый большой затык был с коллекциями, например, строки табличной части документа, на самом деле все просто, но без примера сложно разобраться.

    Reply
  2. PLAstic

    (1) На этом форуме их полно уже. Напишите лучше про какие-нибудь интересные нюансы вроде:

    1) Как с помощью XDTO сформировать массив из нуля элементов и выгрузить его в JSON.

    2) Как недопустить платформенного эксепшна при парсинге JSON по XDTO при наличии в нём пустого массива.

    Я откровенно задолбался общаться с 1Сниками, не хотят они регистрировать эти ошибки. Уже всё им прислал, даже ссыль на RFC с переводом.

    Reply
  3. plevakin

    А если есть схема не в XSD а в таком виде

    <?xml version=»1.0″ encoding=»utf-8″ ?>

    — <edmx:Edmx Version=»4.0″ xmlns:edmx=»http://docs.oasis-open.org/odata/ns/edmx»>

    — <edmx:DataServices>

    — <Schema Namespace=»Request.WebAPI.Models.V1″ xmlns=»http://docs.oasis-open.org/odata/ns/edm»>

    — <EntityType Name=»Request»>

    — <Key>

    <PropertyRef Name=»Id» />

    </Key>

    Ее можно как-то прочитать средствами 1C?

    Reply
  4. plevakin

    (2) ПрочитатьJSON(ЧтениеJSON,истина) не предлагать?

    Reply
  5. PLAstic

    (4) Конечно, нет. Мне же надо преобразовать к типу объекта со всеми вытекающими проверками структуры пакета и значений.

    Reply
  6. PLAstic

    (3) Загрузи XDTO-пакет, ссылка у тебя есть. Если лень искать, то вот:

    http://docs.oasis-open.org/odata/odata-csdl-xml/v4.01/cs01/schemas/edm.xsd

    http://docs.oasis-open.org/odata/odata-csdl-xml/v4.01/cs01/schemas/edmx.xsd

    Reply
  7. axae

    (1)

    о статье отметил бы:

    — Непонятно зачем программно делать экспорт сх

    А если поменяется конфа бухгалтерии? Допускаю, что это редко, но всё же… Тут просто интересно было, на самом деле конечно можно и так взять откуда-нибудь. Конкретно в моей ситуации схему бухгалтерии отдает httpСервис.

    Reply
  8. axae

    (1)

    XDTO для чайников

    — Я как раз тот чайник! Но спросить, к сожалению, не у кого все эти нюансы, да и элементарные вещи.

    Reply
  9. user1302499

    И как понять где какой код должен быть?

    Reply
  10. axae

    (9) В базе-источнике получаем фабрику XDTO на основании схем базы источника. Создаем объекты с использованием типов, предоставляемых этой фабриком и записываем их в файл (ну или еще куда-нибудь). В базе-приемнике просто читаем объекты из файла. Это вкратце так.

    Reply

Leave a Comment

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