При настройке обмена данными между десктопными конфигурациями 1С и мобильными приложениями (например, «1С:Управление предприятием» / «1С:УНФ» и «1С:Заказы») разработчики часто сталкиваются с ситуацией, когда состав реквизитов документов различается. Попытка переопределить стандартную сериализацию через ручную запись и чтение XML нередко приводит к ошибке: «Ошибка при вызове метода контекста (ЗакончитьЧтение)». Разберем по шагам, почему возникает этот сбой и как правильно организовать обмен данными с произвольной структурой.
Проанализируем механику работы инфраструктуры сообщений обмена в платформе 1С:Предприятие. Когда мы используем чтение сообщения обмена, платформа ожидает строгой последовательности перемещения курсора по XML-документу. Ошибка на вызове метода ЧтениеСообщения.ЗакончитьЧтение() практически всегда свидетельствует о нарушении структуры чтения потока. Рассмотрим ключевые факторы, приводящие к сбою:
ВозможностьЧтенияXML(ЧтениеXML) проверяет, является ли текущий узел стандартным типом метаданных текущей конфигурации. Если в XML передан кастомный тег (например, CatalogObject.Заказы.Вручную), стандартная функция возвращает Ложь. Цикл чтения данных из сообщения прерывается до того, как прочитаны все узлы, и курсор XML не доходит до закрывающего тега тела сообщения.ПрочитатьXML(ЧтениеXML) автоматически считывает элемент целиком и смещает указатель дальше. Если разработчик дополнительно вызывает явный метод ЧтениеXML.Прочитать() или оставляет часть записанных тегов невычитанными (закомментированными в процедуре чтения), указатель сбивается, пропуская закрывающие теги объектов.ЗаписатьXMLЗаказы формируется 10 узлов, а при чтении в ПрочитатьXMLЗаказы часть строк закомментирована, поток XML остается недочитанным внутри элемента документа, что разрушает разбор всей последующей цепочки данных.Рассмотрим, как правильно организовать ручной парсинг XML-узлов в типовом цикле обработки сообщения. Первым делом заменим стандартную проверку возможности чтения на собственную функцию-обертку, которая умеет распознавать кастомный тип.
Определим функцию проверки возможности чтения данных:
Функция ВозможностьЧтенияДанных(ЧтениеXML) Экспорт
ТипXML = ПолучитьXMLТип(ЧтениеXML);
Если ТипXML = Неопределено Тогда
Возврат Ложь;
КонецЕсли;
// Проверяем наш нестандартный тип
Если ТипXML.ИмяТипа = "CatalogObject.Заказы.Вручную" И ТипXML.URIПространстваИмен = "" Тогда
Возврат Истина;
КонецЕсли;
// Для стандартных объектов используем системную проверку
Возврат ВозможностьЧтенияXML(ЧтениеXML);
КонецФункции
Теперь основной цикл обработки сообщения должен вызывать не системный ВозможностьЧтенияXML, а нашу функцию ВозможностьЧтенияДанных. Разберем реализацию процедуры ручного чтения документа. Обратите внимание: все записанные в XML поля должны быть обязательно прочитаны, даже если они не нужны в приемнике:
Функция ПрочитатьXMLЗаказы(ЧтениеXML) Экспорт
Если ЧтениеXML.ТипУзла <> ТипУзлаXML.НачалоЭлемента Тогда
ВызватьИсключение "Ошибка структуры XML: ожидается НачалоЭлемента";
КонецЕсли;
// Входим внутрь контейнера объекта
ЧтениеXML.Прочитать();
// Читаем УИД и ищем объект
ЗаказСсылка = ПрочитатьXML(ЧтениеXML);
ДокСсылка = Документы.Заказ.ПолучитьСсылку(Новый УникальныйИдентификатор(ЗаказСсылка));
Заказ = ДокСсылка.ПолучитьОбъект();
Если Заказ = Неопределено Тогда
Заказ = Документы.Заказ.СоздатьДокумент();
Заказ.УстановитьСсылкуНового(ДокСсылка);
КонецЕсли;
// Считываем реквизиты строго в порядке их записи
Заказ.ПометкаУдаления = ПрочитатьXML(ЧтениеXML);
Заказ.Дата = ПрочитатьXML(ЧтениеXML);
Заказ.Номер = ПрочитатьXML(ЧтениеXML);
Заказ.Проведен = ПрочитатьXML(ЧтениеXML);
// Если реквизит не нужен или заполняется иначе, считываем значение в переменную
ПропущенныйПокупатель = ПрочитатьXML(ЧтениеXML);
ПропущенныйСклад = ПрочитатьXML(ЧтениеXML);
ПропущеннаяВалюта = ПрочитатьXML(ЧтениеXML);
ПропущенныйВидЦен = ПрочитатьXML(ЧтениеXML);
ПропущеннаяОрг = ПрочитатьXML(ЧтениеXML);
ПропущенноеСостояние = ПрочитатьXML(ЧтениеXML);
ПропущенныйАвтор = ПрочитатьXML(ЧтениеXML);
Заказ.СуммаДокумента = ПрочитатьXML(ЧтениеXML);
// Проверяем, что курсор остановился на закрывающем теге
Если ЧтениеXML.ТипУзла <> ТипУзлаXML.КонецЭлемента Тогда
ВызватьИсключение "Ошибка формата XML: нарушен порядок чтения тегов";
КонецЕсли;
// Переходим к следующему узлу за пределы текущего элемента
ЧтениеXML.Прочитать();
Возврат Заказ;
КонецФункции
Прямая ручная работа с потоком через ЧтениеXML крайне чувствительна к любым изменениям формата и наличию самозакрывающихся тегов. Более надежным способом передачи данных с несовпадающей структурой является использование механизма XDTO. Рассмотрим этот подход подробнее:
ФабрикаXDTO.Создать(), заполняем его свойства и выгружаем в XML вызовом ФабрикаXDTO.ЗаписатьXML(ЗаписьXML, ОбъектXDTO).ФабрикаXDTO.ПрочитатьXML(ЧтениеXML). Платформа самостоятельно контролирует правильность закрытия всех вложенных узлов, что полностью устраняет риск рассинхронизации указателя.Для взаимодействия мобильных приложений с центральной информационной базой стандартный механизм планов обмена на XML часто оказывается избыточным и сложным в отладке. Посмотрим на альтернативный архитектурный вариант:
ЧтениеJSON) без блокировок контекста чтения сообщений обмена.Такой подход обеспечивает независимость релизов мобильного клиента и основной базы, а также упрощает диагностику при добавлении новых реквизитов.