При организации обмена между мобильным приложением и центральной информационной базой 1С разработчики часто сталкиваются с ситуацией, когда состав метаданных в базах различается. Например, в мобильной базе создается упрощенный документ всего с несколькими реквизитами, а в центральной базе соответствующая сущность содержит сотни реквизитов или имеет совершенно другое имя и структуру. Попытка передать такой объект через стандартный механизм сериализации приводит к исключению и прерыванию чтения пакета. Разберем причины возникновения этой ошибки и подробно рассмотрим способы её решения.
Проанализируем механику работы стандартного механизма обмена. Когда мы используем СериализаторXDTO и его метод ПрочитатьXML(), платформа опирается на пространство имен конфигурации (http://v8.1c.ru/8.1/data/enterprise/current-config). Этот механизм жестко привязан к структуре метаданных конкретной информационной базы.
Если во входящем потоке ЧтениеXML встречается узел объекта, отсутствующего в дереве метаданных базы-приемника, СериализаторXDTO генерирует исключение «Ошибка разбора XML». Обычный перехват через конструкцию Попытка ... Исключение здесь не сработает: при возникновении сбоя десериализации указатель потока в объекте ЧтениеXML сдвигается непредсказуемо, что делает невозможным продолжение корректного чтения остальных данных из файла сообщения. Таким образом, неизвестный объект необходимо корректно идентифицировать и обработать до момента вызова стандартной десериализации.
Рассмотрим наиболее надежный подход для последовательного разбора входящего потока без изменения глобальных схем. Мы откажемся от автоматической вычитки всего пакета одной командой и организуем пошаговое чтение узлов.
Разберем логику работы по шагам:
ЧтениеXML и перебираем узлы в цикле.ТипУзлаXML.НачалоЭлемента проверяем, может ли платформа десериализовать его стандартным образом с помощью метода СериализаторXDTO.ВозможностьЧтенияXML(ЧтениеXML).Истина, считываем объект штатным методом СериализаторXDTO.ПрочитатьXML(ЧтениеXML).Ложь (или имя узла соответствует нашему специфичному документу), считываем этот элемент с помощью модели DOM через ПостроительDOM. Это позволяет изолированно прочитать узел, продвинуть указатель потока и разобрать реквизиты вручную.Посмотрим на пример реализации алгоритма на стороне центральной базы:
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.ОткрытьФайл(ИмяФайлаОбмена);
Пока ЧтениеXML.Прочитать() Цикл
Если ЧтениеXML.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда
Если ЧтениеXML.ЛокальноеИмя = "DocumentObject.УпрощенныйДокумент" Тогда
// Узел нестандартного документа считываем в DOM-модель
Построитель = Новый ПостроительDOM;
ДокументDOM = Построитель.Прочитать(ЧтениеXML);
// Создаем полноценный целевой документ в базе
НовыйДокумент = Документы.СложныйЦентральныйДокумент.СоздатьДокумент();
НовыйДокумент.Дата = ТекущаяДатаСеанса();
// Извлекаем значения дочерних элементов
УзлыРеквизитов = ДокументDOM.ЭлементДокумента.ДочерниеУзлы;
Для Каждого Узел Из УзлыРеквизитов Цикл
Если Узел.ТипУзла = ТипУзлаDOM.Элемент Тогда
Если Узел.ИмяУзла = "Сумма" Тогда
НовыйДокумент.СуммаДокумента = Число(Узел.ТекстовоеСодержимое);
ИначеЕсли Узел.ИмяУзла = "Комментарий" Тогда
НовыйДокумент.Комментарий = Узел.ТекстовоеСодержимое;
КонецЕсли;
КонецЕсли;
КонецЦикла;
НовыйДокумент.Записать(РежимЗаписиДокумента.Запись);
ИначеЕсли СериализаторXDTO.ВозможностьЧтенияXML(ЧтениеXML) Тогда
// Штатные объекты вычитываем стандартным сериализатором
ДанныеОбъекта = СериализаторXDTO.ПрочитатьXML(ЧтениеXML);
ДанныеОбъекта.Записать();
КонецЕсли;
КонецЕсли;
КонецЦикла;
ЧтениеXML.Закрыть();
Рассмотрим архитектурно более чистое решение, которое полностью исключает ручной парсинг XML-тегов через DOM и опирается на строгую типизацию. Для этого создадим общий облегченный XDTO-пакет, который описывает формат передаваемых данных.
Разберем по шагам, как настроить такую передачу:
http://company.com/mobileExchange).Номер, Дата, Сумма, КонтрагентКод).ФабрикаXDTO.Создать(), заполняем свойства и сериализуем в поток с помощью ФабрикаXDTO.ЗаписатьXML().Фабрика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.Комментарий;
ЦелевойДокумент.Записать(РежимЗаписиДокумента.Запись);
КонецЕсли;
Если нет желания добавлять отдельный XDTO-пакет в обе конфигурации, проанализируем универсальный способ передачи произвольных данных. Объект платформы Структура поддерживается стандартным сериализатором 1С «из коробки» в любой конфигурации.
На мобильном устройстве мы можем собрать необходимые поля документа в обычную структуру или упаковать их в JSON-строку с помощью объектов ЗаписьJSON и СериализаторXDTO. В центральной базе полученная структура легко десериализуется, а её поля напрямую переносятся в реквизиты сложного документа-приемника.
Таким образом, для обмена объектами с несовпадающим набором реквизитов нет необходимости полностью дублировать тяжелые структуры метаданных в мобильном клиенте — достаточно использовать пошаговый контроль потока через ВозможностьЧтенияXML, чтение DOM либо выделенные типы ФабрикаXDTO.