При настройке обмена данными между конфигурациями на платформе 1С с использованием «1С:Конвертации данных 2.1» (КД) разработчики часто сталкиваются с необходимостью передать сопутствующую информацию, которая напрямую не сопоставляется со свойствами объектов. Для этого нередко прибегают к низкоуровневой выгрузке данных через ручное создание подчиненных XML-узлов в файле обмена.
Однако этот подход таит в себе скрытые угрозы. Типичная ситуация: выгрузка проходит успешно, файл формируется без ошибок, но при загрузке в базу-приемник платформа выдает критическую ошибку «Ошибка формата файла обмена», указывая на ваш кастомный узел, после чего аварийно прекращает чтение всех последующих объектов. В этой статье мы подробно разберем, почему возникает эта ошибка, как исправить логику чтения XML-потока и как реализовать эту же задачу по современным стандартам разработки без ручного разбора XML.
Выясним причины сбоя, проанализируем поведение XML-парсера платформы 1С и рассмотрим два метода решения проблемы.
Рассмотрим сценарий, с которым столкнулся разработчик. При выгрузке документа (например, ПереносДанных) в обработчике события ПКО создается подчиненный узел с атрибутами:
Узел = СоздатьУзел("ДанныеОФизЛице");
УстановитьАтрибут(Узел, "Период", ВходящиеДанные.ПериодРегистрации);
УстановитьАтрибут(Узел, "СтраховойНомерПФР", ВходящиеДанные.СтраховойНомерПФР);
ДобавитьПодчиненный(Приемник, Узел);
В результате в XML-файле формируется вполне валидный, с точки зрения стандартов XML, самозакрывающийся тег:
<ДанныеОФизЛице СтраховойНомерПФР="111-111-11 11" Период="2020-02-01T00:00:00"/>
Для чтения этого узла в базе-приемнике в обработчике «При загрузке» (или «Перед загрузкой») пишется следующий цикл обхода:
Пока ФайлОбмена.Прочитать() Цикл
ИмяУзла = ФайлОбмена.ЛокальноеИмя;
ТипУзла = ФайлОбмена.ТипУзла;
Если ИмяУзла = "ДанныеОФизЛице" и ТипУзла = одТипУзлаXML_НачалоЭлемента Тогда
Период = одАтрибут(ФайлОбмена, одТипДата, "Период");
СтраховойНомерПФР = одАтрибут(ФайлОбмена, одТипСтрока, "СтраховойНомерПФР");
ИначеЕсли ИмяУзла = "ДанныеОФизЛице" И ТипУзла = одТипУзлаXML_КонецЭлемента Тогда
Прервать;
КонецЕсли;
КонецЦикла;
На первый взгляд код выглядит логичным: мы заходим в цикл, ищем начало нашего элемента, считываем атрибуты, а при встрече закрывающего тега завершаем чтение с помощью оператора Прервать, чтобы вернуть управление стандартному парсеру Конвертации данных. Но именно здесь кроется критическая ошибка.
Главная техническая проблема заключается в поведении встроенного в платформу 1С XML-парсера (объекта ЧтениеXML, на котором базируется переменная ФайлОбмена). Наш узел <ДанныеОФизЛице ... /> не имеет отдельного закрывающего тега — он пустой и закрывается сам на себе (символом />).
Проанализируем по шагам, как парсер обрабатывает этот узел:
ФайлОбмена.Прочитать() позиционируется на узле ДанныеОФизЛице.ТипУзла принимает значение одТипУзлаXML_НачалоЭлемента.Период и СтраховойНомерПФР.одТипУзлаXML_КонецЭлемента) для этого узла в потоке XML никогда не возникнет! Свойство ФайлОбмена.Пустой() для него равно Истина.ФайлОбмена.Прочитать() сдвигает указатель на следующий элемент файла обмена (например, <НаборЗаписей>)."ДанныеОФизЛице", условия внутри цикла не выполняются, и цикл продолжается, последовательно считывая («проглатывая») все внутренние теги объекта: свойства, записи регистров, табличные части.Когда ядро КД возобновляет стандартное чтение объекта, указатель файла обмена физически сдвинут далеко вперед. Структурные теги документа уже прочитаны и пропущены в вашем цикле. Платформа ожидает увидеть свойства объекта, а видит закрывающие теги или другие сущности. В результате генерируется лавинообразная ошибка формата файла обмена.
В «1С:Конвертации данных 2.1» ручное чтение подчиненных узлов через ФайлОбмена.Прочитать() должно выполняться строго в обработчике «Перед загрузкой» правил конвертации объектов (ПКО), а не в «При загрузке».
В момент вызова обработчика «Перед загрузкой» парсер только что прочитал заголовок объекта <Объект> и готов читать его содержимое. Вы можете безопасно перехватить поток. Обработчик «При загрузке» выполняется на более позднем этапе, когда свойства объекта уже сопоставлены и прочитаны платформой, и вмешательство в последовательное чтение XML гарантированно нарушает логику КД.
Примечание по настройкам поиска: Автор темы отметил, что ошибка уходит, если включить поиск по внутреннему идентификатору или настроить поля поиска. Это происходит потому, что КД меняет внутренний алгоритм позиционирования XML-потока при сопоставлении объектов. Однако это лишь маскирует проблему десинхронизации указателя чтения — сама логическая ошибка проглатывания тегов в коде остается «миной замедленного действия».
---
Если вам по каким-то причинам критически важно использовать именно ручное создание и разбор XML-узлов, необходимо научить код корректно обрабатывать пустые (самозакрывающиеся) элементы. Для этого используется проверка свойства Пустой() объекта чтения XML. Модифицируем код загрузки следующим образом:
Пока ФайлОбмена.Прочитать() Цикл
ИмяУзла = ФайлОбмена.ЛокальноеИмя;
ТипУзла = ФайлОбмена.ТипУзла;
Если ИмяУзла = "ДанныеОФизЛице" И ТипУзла = одТипУзлаXML_НачалоЭлемента Тогда
// Читаем атрибуты
Период = одАтрибут(ФайлОбмена, одТипДата, "Период");
СтраховойНомерПФР = одАтрибут(ФайлОбмена, одТипСтрока, "СтраховойНомерПФР");
// КРИТИЧЕСКИ ВАЖНО: Если узел самозакрывающийся, прерываем цикл немедленно!
Если ФайлОбмена.Пустой() Тогда
Прервать;
КонецЕсли;
ИначеЕсли ИмяУзла = "ДанныеОФизЛице" И ТипУзла = одТипУзлаXML_КонецЭлемента Тогда
// Сработает только если узел имел явный закрывающий тег </ДанныеОФизЛице>
Прервать;
КонецЕсли;
КонецЦикла;
Этот простой шаг гарантирует, что как только парсер прочитает атрибуты самозакрывающегося узла, управление вернется платформе без повреждения последующей структуры XML.
---
Написание низкоуровневого кода для работы с XML-документами вручную — устаревший подход, унаследованный из времен обменов с версией 1С:Предприятие 7.7. Современный и полностью безопасный стандарт разработки в «1С:Конвертации данных 2.1» для передачи сопутствующей информации — это использование штатного механизма Параметров объекта.
Параметры автоматически сериализуются платформой, не требуют ручного разбора XML, не зависят от настроек поиска и полностью защищены от ошибок десинхронизации парсера.
Рассмотрим по шагам, как перевести вашу задачу на использование параметров объекта:
Откроем правила конвертации объектов (ПКО) для нашего документа (например, ПереносДанных) в конфигурации «Конвертация данных»:
ПериодРегистрации и СтраховойНомерПФР.В ПКО нашего документа найдем обработчик события «Перед выгрузкой» и пропишем заполнение созданных параметров. Данные будут автоматически упакованы платформой в стандартную XML-структуру:
Параметры.Вставить("ПериодРегистрации", Источник.ПериодРегистрации);
Параметры.Вставить("СтраховойНомерПФР", ВходящиеДанные.СтраховойНомерПФР);
В XML-файле обмена это автоматически сгенерирует безопасные теги вида <Параметр Имя="..." Тип="..."><Значение>...</Значение></Параметр>, обработку которых платформа КД берет на себя.
В ПКО документа перейдем к обработчику события «После загрузки». Вся переданная информация будет доступна в готовом виде в структуре ПараметрыОбъекта. Нам больше не нужно запускать циклы чтения XML-потока:
Если ПараметрыОбъекта <> Неопределено Тогда
Период = Неопределено;
СтраховойНомерПФР = "";
ПараметрыОбъекта.Свойство("ПериодРегистрации", Период);
ПараметрыОбъекта.Свойство("СтраховойНомерПФР", СтраховойНомерПФР);
// Выполняем логику поиска или постобработки документа ПереносДанных
Если ЗначениеЗаполнено(Период) И ЗначениеЗаполнено(СтраховойНомерПФР) Тогда
// Ваш алгоритм поиска и привязки
КонецЕсли;
КонецЕсли;
Использование параметров объекта исключает возникновение ошибок формата файла обмена, делает код чистым, читаемым и устойчивым к изменениям конфигурации. Старайтесь избегать ручной модификации XML-потока везде, где это возможно, доверяя чтение и запись стандартным механизмам «1С:Конвертации данных».