Как исправить ошибку «ЗакончитьЧтение» при обмене данными между 1С и мобильным приложением?

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

При настройке обмена данными между десктопными конфигурациями 1С и мобильными приложениями (например, «1С:Управление предприятием» / «1С:УНФ» и «1С:Заказы») разработчики часто сталкиваются с ситуацией, когда состав реквизитов документов различается. Попытка переопределить стандартную сериализацию через ручную запись и чтение XML нередко приводит к ошибке: «Ошибка при вызове метода контекста (ЗакончитьЧтение)». Разберем по шагам, почему возникает этот сбой и как правильно организовать обмен данными с произвольной структурой.

Выясним причину ошибки

Проанализируем механику работы инфраструктуры сообщений обмена в платформе 1С:Предприятие. Когда мы используем чтение сообщения обмена, платформа ожидает строгой последовательности перемещения курсора по XML-документу. Ошибка на вызове метода ЧтениеСообщения.ЗакончитьЧтение() практически всегда свидетельствует о нарушении структуры чтения потока. Рассмотрим ключевые факторы, приводящие к сбою:

  1. Преждевременный выход из цикла чтения. Метод платформы ВозможностьЧтенияXML(ЧтениеXML) проверяет, является ли текущий узел стандартным типом метаданных текущей конфигурации. Если в XML передан кастомный тег (например, CatalogObject.Заказы.Вручную), стандартная функция возвращает Ложь. Цикл чтения данных из сообщения прерывается до того, как прочитаны все узлы, и курсор XML не доходит до закрывающего тега тела сообщения.
  2. Рассинхронизация указателя чтения XML. Метод ПрочитатьXML(ЧтениеXML) автоматически считывает элемент целиком и смещает указатель дальше. Если разработчик дополнительно вызывает явный метод ЧтениеXML.Прочитать() или оставляет часть записанных тегов невычитанными (закомментированными в процедуре чтения), указатель сбивается, пропуская закрывающие теги объектов.
  3. Несовпадение количества записанных и прочитанных полей. Если при выгрузке в процедуре ЗаписатьXMLЗаказы формируется 10 узлов, а при чтении в ПрочитатьXMLЗаказы часть строк закомментирована, поток XML остается недочитанным внутри элемента документа, что разрушает разбор всей последующей цепочки данных.

Решение 1. Корректировка условия цикла и структуры чтения потока 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.Прочитать();

    Возврат Заказ;
КонецФункции

Решение 2. Использование фабрики XDTO

Прямая ручная работа с потоком через ЧтениеXML крайне чувствительна к любым изменениям формата и наличию самозакрывающихся тегов. Более надежным способом передачи данных с несовпадающей структурой является использование механизма XDTO. Рассмотрим этот подход подробнее:

  1. Создаем собственный XDTO-пакет, описывающий универсальную структуру заказа (поля шапки, табличные части), не привязанную к метаданным конкретной конфигурации.
  2. На стороне источника формируем объект XDTO с помощью метода ФабрикаXDTO.Создать(), заполняем его свойства и выгружаем в XML вызовом ФабрикаXDTO.ЗаписатьXML(ЗаписьXML, ОбъектXDTO).
  3. На стороне приемника выполняем чтение через ФабрикаXDTO.ПрочитатьXML(ЧтениеXML). Платформа самостоятельно контролирует правильность закрытия всех вложенных узлов, что полностью устраняет риск рассинхронизации указателя.

Решение 3. Переход на HTTP-сервисы и JSON

Для взаимодействия мобильных приложений с центральной информационной базой стандартный механизм планов обмена на XML часто оказывается избыточным и сложным в отладке. Посмотрим на альтернативный архитектурный вариант:

  1. В центральной базе публикуется HTTP-сервис с методами передачи и приема заказов.
  2. План обмена используется исключительно для регистрации изменений (хранения ссылок на измененные объекты).
  3. Тело запросов формируется в формате JSON. При получении JSON-пакета мобильное приложение разбирает его стандартными средствами платформы (объект ЧтениеJSON) без блокировок контекста чтения сообщений обмена.

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

← На главную