Как исправить ошибку формата файла обмена при чтении подчиненного узла в 1С:Конвертации данных

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

При настройке обмена данными между конфигурациями на платформе 1С с использованием «1С:Конвертации данных 2.1» (КД) разработчики часто сталкиваются с необходимостью передать сопутствующую информацию, которая напрямую не сопоставляется со свойствами объектов. Для этого нередко прибегают к низкоуровневой выгрузке данных через ручное создание подчиненных XML-узлов в файле обмена.

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

Выясним причины сбоя, проанализируем поведение XML-парсера платформы 1С и рассмотрим два метода решения проблемы.

Анализ исходной проблемы и кода

Рассмотрим сценарий, с которым столкнулся разработчик. При выгрузке документа (например, ПереносДанных) в обработчике события ПКО создается подчиненный узел с атрибутами:


Узел = СоздатьУзел("ДанныеОФизЛице"); 
УстановитьАтрибут(Узел, "Период", ВходящиеДанные.ПериодРегистрации); 
УстановитьАтрибут(Узел, "СтраховойНомерПФР", ВходящиеДанные.СтраховойНомерПФР); 
ДобавитьПодчиненный(Приемник, Узел); 

В результате в XML-файле формируется вполне валидный, с точки зрения стандартов XML, самозакрывающийся тег:

<ДанныеОФизЛице СтраховойНомерПФР="111-111-11 11" Период="2020-02-01T00:00:00"/>

Для чтения этого узла в базе-приемнике в обработчике «При загрузке» (или «Перед загрузкой») пишется следующий цикл обхода:


Пока ФайлОбмена.Прочитать() Цикл 
    ИмяУзла = ФайлОбмена.ЛокальноеИмя; 
    ТипУзла = ФайлОбмена.ТипУзла; 
    Если ИмяУзла = "ДанныеОФизЛице" и ТипУзла = одТипУзлаXML_НачалоЭлемента Тогда 
        Период = одАтрибут(ФайлОбмена, одТипДата, "Период"); 
        СтраховойНомерПФР = одАтрибут(ФайлОбмена, одТипСтрока, "СтраховойНомерПФР"); 
    ИначеЕсли ИмяУзла = "ДанныеОФизЛице" И ТипУзла = одТипУзлаXML_КонецЭлемента Тогда 
        Прервать; 
    КонецЕсли; 
КонецЦикла; 

На первый взгляд код выглядит логичным: мы заходим в цикл, ищем начало нашего элемента, считываем атрибуты, а при встрече закрывающего тега завершаем чтение с помощью оператора Прервать, чтобы вернуть управление стандартному парсеру Конвертации данных. Но именно здесь кроется критическая ошибка.

Скрытая причина №1: Ловушка самозакрывающегося тега (Self-Closing Tag)

Главная техническая проблема заключается в поведении встроенного в платформу 1С XML-парсера (объекта ЧтениеXML, на котором базируется переменная ФайлОбмена). Наш узел <ДанныеОФизЛице ... /> не имеет отдельного закрывающего тега — он пустой и закрывается сам на себе (символом />).

Проанализируем по шагам, как парсер обрабатывает этот узел:

  1. Метод ФайлОбмена.Прочитать() позиционируется на узле ДанныеОФизЛице.
  2. Свойство ТипУзла принимает значение одТипУзлаXML_НачалоЭлемента.
  3. Первое условие в нашем коде срабатывает, успешно считывая значения атрибутов Период и СтраховойНомерПФР.
  4. Поскольку тег самозакрывающийся, событие окончания элемента (одТипУзлаXML_КонецЭлемента) для этого узла в потоке XML никогда не возникнет! Свойство ФайлОбмена.Пустой() для него равно Истина.
  5. На следующей итерации цикла метод ФайлОбмена.Прочитать() сдвигает указатель на следующий элемент файла обмена (например, <НаборЗаписей>).
  6. Поскольку имя нового узла не равно "ДанныеОФизЛице", условия внутри цикла не выполняются, и цикл продолжается, последовательно считывая («проглатывая») все внутренние теги объекта: свойства, записи регистров, табличные части.
  7. Цикл завершится либо когда файл закончится, либо когда будет встречен конец самого документа. В этот момент управление возвращается ядру Конвертации данных.

Когда ядро КД возобновляет стандартное чтение объекта, указатель файла обмена физически сдвинут далеко вперед. Структурные теги документа уже прочитаны и пропущены в вашем цикле. Платформа ожидает увидеть свойства объекта, а видит закрывающие теги или другие сущности. В результате генерируется лавинообразная ошибка формата файла обмена.

Скрытая причина №2: Неверный выбор события для чтения

В «1С:Конвертации данных 2.1» ручное чтение подчиненных узлов через ФайлОбмена.Прочитать() должно выполняться строго в обработчике «Перед загрузкой» правил конвертации объектов (ПКО), а не в «При загрузке».

В момент вызова обработчика «Перед загрузкой» парсер только что прочитал заголовок объекта <Объект> и готов читать его содержимое. Вы можете безопасно перехватить поток. Обработчик «При загрузке» выполняется на более позднем этапе, когда свойства объекта уже сопоставлены и прочитаны платформой, и вмешательство в последовательное чтение XML гарантированно нарушает логику КД.

Примечание по настройкам поиска: Автор темы отметил, что ошибка уходит, если включить поиск по внутреннему идентификатору или настроить поля поиска. Это происходит потому, что КД меняет внутренний алгоритм позиционирования XML-потока при сопоставлении объектов. Однако это лишь маскирует проблему десинхронизации указателя чтения — сама логическая ошибка проглатывания тегов в коде остается «миной замедленного действия».

---

Решение 1: Исправление низкоуровневого кода чтения XML

Если вам по каким-то причинам критически важно использовать именно ручное создание и разбор XML-узлов, необходимо научить код корректно обрабатывать пустые (самозакрывающиеся) элементы. Для этого используется проверка свойства Пустой() объекта чтения XML. Модифицируем код загрузки следующим образом:


Пока ФайлОбмена.Прочитать() Цикл 
    ИмяУзла = ФайлОбмена.ЛокальноеИмя; 
    ТипУзла = ФайлОбмена.ТипУзла; 
    
    Если ИмяУзла = "ДанныеОФизЛице" И ТипУзла = одТипУзлаXML_НачалоЭлемента Тогда 
        // Читаем атрибуты
        Период = одАтрибут(ФайлОбмена, одТипДата, "Период"); 
        СтраховойНомерПФР = одАтрибут(ФайлОбмена, одТипСтрока, "СтраховойНомерПФР"); 
        
        // КРИТИЧЕСКИ ВАЖНО: Если узел самозакрывающийся, прерываем цикл немедленно!
        Если ФайлОбмена.Пустой() Тогда 
            Прервать; 
        КонецЕсли;
        
    ИначеЕсли ИмяУзла = "ДанныеОФизЛице" И ТипУзла = одТипУзлаXML_КонецЭлемента Тогда 
        // Сработает только если узел имел явный закрывающий тег </ДанныеОФизЛице>
        Прервать; 
    КонецЕсли; 
КонецЦикла; 

Этот простой шаг гарантирует, что как только парсер прочитает атрибуты самозакрывающегося узла, управление вернется платформе без повреждения последующей структуры XML.

---

Решение 2 (Рекомендуемое): Использование параметров объекта

Написание низкоуровневого кода для работы с XML-документами вручную — устаревший подход, унаследованный из времен обменов с версией 1С:Предприятие 7.7. Современный и полностью безопасный стандарт разработки в «1С:Конвертации данных 2.1» для передачи сопутствующей информации — это использование штатного механизма Параметров объекта.

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

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

Шаг 1. Настройка параметров в ПКО (в базе Конвертации данных)

Откроем правила конвертации объектов (ПКО) для нашего документа (например, ПереносДанных) в конфигурации «Конвертация данных»:

  1. Перейдем на вкладку «Параметры».
  2. Добавим две строки (два параметра): ПериодРегистрации и СтраховойНомерПФР.
  3. Для каждого параметра обязательно установим флаг «Передавать при выгрузке» (это указывает КД, что параметры нужно записать в XML-файл вместе с объектом).

Шаг 2. Заполнение параметров при выгрузке

В ПКО нашего документа найдем обработчик события «Перед выгрузкой» и пропишем заполнение созданных параметров. Данные будут автоматически упакованы платформой в стандартную XML-структуру:


Параметры.Вставить("ПериодРегистрации", Источник.ПериодРегистрации);
Параметры.Вставить("СтраховойНомерПФР", ВходящиеДанные.СтраховойНомерПФР);

В XML-файле обмена это автоматически сгенерирует безопасные теги вида <Параметр Имя="..." Тип="..."><Значение>...</Значение></Параметр>, обработку которых платформа КД берет на себя.

Шаг 3. Получение параметров при загрузке

В ПКО документа перейдем к обработчику события «После загрузки». Вся переданная информация будет доступна в готовом виде в структуре ПараметрыОбъекта. Нам больше не нужно запускать циклы чтения XML-потока:


Если ПараметрыОбъекта <> Неопределено Тогда
    Период = Неопределено;
    СтраховойНомерПФР = "";
    
    ПараметрыОбъекта.Свойство("ПериодРегистрации", Период);
    ПараметрыОбъекта.Свойство("СтраховойНомерПФР", СтраховойНомерПФР);
    
    // Выполняем логику поиска или постобработки документа ПереносДанных
    Если ЗначениеЗаполнено(Период) И ЗначениеЗаполнено(СтраховойНомерПФР) Тогда
        // Ваш алгоритм поиска и привязки
    КонецЕсли;
КонецЕсли;

Резюме

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

← На главную