Как передать через XML данные объекта, которого нет в метаданных базы-приемника 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

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

Почему возникает ошибка разбора XML при стандартной десериализации

Проанализируем механику работы стандартного механизма обмена. Когда мы используем СериализаторXDTO и его метод ПрочитатьXML(), платформа опирается на пространство имен конфигурации (http://v8.1c.ru/8.1/data/enterprise/current-config). Этот механизм жестко привязан к структуре метаданных конкретной информационной базы.

Если во входящем потоке ЧтениеXML встречается узел объекта, отсутствующего в дереве метаданных базы-приемника, СериализаторXDTO генерирует исключение «Ошибка разбора XML». Обычный перехват через конструкцию Попытка ... Исключение здесь не сработает: при возникновении сбоя десериализации указатель потока в объекте ЧтениеXML сдвигается непредсказуемо, что делает невозможным продолжение корректного чтения остальных данных из файла сообщения. Таким образом, неизвестный объект необходимо корректно идентифицировать и обработать до момента вызова стандартной десериализации.

Способ 1. Пошаговый разбор XML с проверкой возможности чтения и использованием DOM

Рассмотрим наиболее надежный подход для последовательного разбора входящего потока без изменения глобальных схем. Мы откажемся от автоматической вычитки всего пакета одной командой и организуем пошаговое чтение узлов.

Разберем логику работы по шагам:

  1. Открываем файл с помощью объекта ЧтениеXML и перебираем узлы в цикле.
  2. Для каждого узла типа ТипУзлаXML.НачалоЭлемента проверяем, может ли платформа десериализовать его стандартным образом с помощью метода СериализаторXDTO.ВозможностьЧтенияXML(ЧтениеXML).
  3. Если метод возвращает Истина, считываем объект штатным методом СериализаторXDTO.ПрочитатьXML(ЧтениеXML).
  4. Если метод возвращает Ложь (или имя узла соответствует нашему специфичному документу), считываем этот элемент с помощью модели DOM через ПостроительDOM. Это позволяет изолированно прочитать узел, продвинуть указатель потока и разобрать реквизиты вручную.

Посмотрим на пример реализации алгоритма на стороне центральной базы:


ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.ОткрытьФайл(ИмяФайлаОбмена);

Пока ЧтениеXML.Прочитать() Цикл
    Если ЧтениеXML.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда
        
        Если ЧтениеXML.ЛокальноеИмя = "DocumentObject.УпрощенныйДокумент" Тогда
            // Узел нестандартного документа считываем в DOM-модель
            Построитель = Новый ПостроительDOM;
            ДокументDOM = Построитель.Прочитать(ЧтениеXML);
            
            // Создаем полноценный целевой документ в базе
            НовыйДокумент = Документы.СложныйЦентральныйДокумент.СоздатьДокумент();
            НовыйДокумент.Дата = ТекущаяДатаСеанса();
            
            // Извлекаем значения дочерних элементов
            УзлыРеквизитов = ДокументDOM.ЭлементДокумента.ДочерниеУзлы;
            Для Каждого Узел Из УзлыРеквизитов Цикл
                Если Узел.ТипУзла = ТипУзлаDOM.Элемент Тогда
                    Если Узел.ИмяУзла = "Сумма" Тогда
                        НовыйДокумент.СуммаДокумента = Число(Узел.ТекстовоеСодержимое);
                    ИначеЕсли Узел.ИмяУзла = "Комментарий" Тогда
                        НовыйДокумент.Комментарий = Узел.ТекстовоеСодержимое;
                    КонецЕсли;
                КонецЕсли;
            КонецЦикла;
            
            НовыйДокумент.Записать(РежимЗаписиДокумента.Запись);
            
        ИначеЕсли СериализаторXDTO.ВозможностьЧтенияXML(ЧтениеXML) Тогда
            // Штатные объекты вычитываем стандартным сериализатором
            ДанныеОбъекта = СериализаторXDTO.ПрочитатьXML(ЧтениеXML);
            ДанныеОбъекта.Записать();
        КонецЕсли;
        
    КонецЕсли;
КонецЦикла;

ЧтениеXML.Закрыть();

Способ 2. Использование общего XDTO-пакета

Рассмотрим архитектурно более чистое решение, которое полностью исключает ручной парсинг XML-тегов через DOM и опирается на строгую типизацию. Для этого создадим общий облегченный XDTO-пакет, который описывает формат передаваемых данных.

Разберем по шагам, как настроить такую передачу:

  1. В обеих конфигурациях (в мобильной и в центральной) создаем одинаковый объект метаданных Пакеты XDTO (например, с пространством имен http://company.com/mobileExchange).
  2. Внутри пакета создаем тип объекта, содержащий исключительно те реквизиты, которые нужны для передачи (например, Номер, Дата, Сумма, КонтрагентКод).
  3. На стороне мобильного приложения формируем экземпляр этого типа через ФабрикаXDTO.Создать(), заполняем свойства и сериализуем в поток с помощью ФабрикаXDTO.ЗаписатьXML().
  4. На стороне центральной базы узел считывается через ФабрикаXDTO.ПрочитатьXML(). Поскольку тип зарегистрирован в схеме, платформа безошибочно вернет типизированный объект ОбъектXDTO.

Посмотрим на пример выгрузки данных на мобильном клиенте:


ТипДанных = ФабрикаXDTO.Тип("http://company.com/mobileExchange", "УпрощенныйДокумент");
ОбъектXDTO = ФабрикаXDTO.Создать(ТипДанных);

ОбъектXDTO.Сумма = ДокументВыгрузки.Сумма;
ОбъектXDTO.Комментарий = ДокументВыгрузки.Комментарий;

ФабрикаXDTO.ЗаписатьXML(ЗаписьXML, ОбъектXDTO);

Теперь разберем, как прочитать этот блок в центральной базе при разборе файла обмена:


Если ЧтениеXML.ПространствоИменURI = "http://company.com/mobileExchange" Тогда
    ОбъектXDTO = ФабрикаXDTO.ПрочитатьXML(ЧтениеXML);
    
    ЦелевойДокумент = Документы.СложныйЦентральныйДокумент.СоздатьДокумент();
    ЦелевойДокумент.СуммаДокумента = ОбъектXDTO.Сумма;
    ЦелевойДокумент.Комментарий = ОбъектXDTO.Комментарий;
    ЦелевойДокумент.Записать(РежимЗаписиДокумента.Запись);
КонецЕсли;

Способ 3. Передача через универсальные структуры или JSON

Если нет желания добавлять отдельный XDTO-пакет в обе конфигурации, проанализируем универсальный способ передачи произвольных данных. Объект платформы Структура поддерживается стандартным сериализатором 1С «из коробки» в любой конфигурации.

На мобильном устройстве мы можем собрать необходимые поля документа в обычную структуру или упаковать их в JSON-строку с помощью объектов ЗаписьJSON и СериализаторXDTO. В центральной базе полученная структура легко десериализуется, а её поля напрямую переносятся в реквизиты сложного документа-приемника.

Таким образом, для обмена объектами с несовпадающим набором реквизитов нет необходимости полностью дублировать тяжелые структуры метаданных в мобильном клиенте — достаточно использовать пошаговый контроль потока через ВозможностьЧтенияXML, чтение DOM либо выделенные типы ФабрикаXDTO.

← На главную