При десериализации входящего XML-документа с помощью механизма XDTO разработчики нередко сталкиваются с ошибкой валидации структуры. Платформа 1С аварийно завершает чтение со следующим диагностическим сообщением:
Ошибка преобразования данных XDTO: Чтение объекта типа: {...}sales - Проверка дополнительного свойства: форма: Текст, имя:
Разберем по шагам, почему возникает эта ошибка, как настроить XDTO-пакет для корректного чтения документа и какие альтернативные механизмы платформы лучше использовать, если структура XML во внешней системе регулярно меняется.
Проанализируем ситуацию: в сообщении об ошибке фигурирует родительский тип sales, а не дочерний элемент sale. Выясним главные причины такого поведения парсера 1С:
<sales> и вложенным <sale> содержатся символы перевода строки и пробелы. Парсер 1С воспринимает их как текстовый узел (форма: Текст с пустым именем). Если у типа sales в XDTO-пакете отключен флаг Смешанный, фабрика ожидает исключительно строгие дочерние теги и падает при встрече любого форматирующего текста.Открытый = Истина у типа XDTO позволяет элементу принимать дополнительные свойства, но только если платформа может однозначно определить их типы (например, когда во входящем XML явно указан атрибут xsi:type="..." или свойства заранее описаны в глобальной фабрике). Если сторонняя система передает произвольные теги без указания схемы, фабрика XDTO не сможет их сопоставить.Если требуется продолжить работу именно через ФабрикаXDTO, скорректируем настройки типов в пакете XDTO:
sales свойства Смешанный = Истина и Последовательный = Истина. Это укажет фабрике, что внутри контейнера допустимы текстовые разделители (пробелы, переносы строк), и парсинг не прервется.sale мог принимать произвольные дочерние элементы без строгого описания всех полей в схеме, укажем для свойства sale тип anyType из пространства имен http://www.w3.org/2001/XMLSchema.<sale id="1">Значение</sale>), в типе XDTO свойству для значения необходимо задать параметр Форма = Текст.Когда сторонняя система периодически добавляет новые теги, а нам требуется извлечь только определенный набор реквизитов, наиболее надежным решением является потоковый парсер ЧтениеXML. Он полностью игнорирует нерегламентированные теги и не требует создания жестких схем.
Посмотрим на пример обработки документа в цикле:
Чтение = Новый ЧтениеXML;
Чтение.ОткрытьФайл("C:\exchange\sales.xml");
ТекущаяПродажа = Неопределено;
Пока Чтение.Прочитать() Цикл
// Фиксируем начало элемента sale
Если Чтение.ТипУзла = ТипУзлаXML.НачалоЭлемента И Чтение.Имя = "sale" Тогда
ТекущаяПродажа = Новый Структура("Сумма, Номер, Контрагент");
КонецЕсли;
// Считываем только нужные свойства внутри элемента
Если Чтение.ТипУзла = ТипУзлаXML.НачалоЭлемента И ТекущаяПродажа <> Неопределено Тогда
ИмяРеквизита = Чтение.Имя;
Если ТекущаяПродажа.Свойство(ИмяРеквизита) Тогда
Чтение.Прочитать(); // Переходим к значению внутри тега
Если Чтение.ТипУзла = ТипУзлаXML.Текст Тогда
ТекущаяПродажа[ИмяРеквизита] = Чтение.Значение;
КонецЕсли;
КонецЕсли;
КонецЕсли;
// Фиксируем конец элемента sale и сохраняем результат
Если Чтение.ТипУзла = ТипУзлаXML.КонецЭлемента И Чтение.Имя = "sale" Тогда
Если ТекущаяПродажа <> Неопределено Тогда
// Обрабатываем полученную структуру данных
ЗаписатьПродажуВБазу(ТекущаяПродажа);
ТекущаяПродажа = Неопределено;
КонецЕсли;
КонецЕсли;
КонецЦикла;
Чтение.Закрыть();
Если размер XML-файла невелик (умещается в оперативной памяти), удобно разобрать документ с помощью ПостроительDOM и ДокументDOM. Этот способ позволяет выбирать только целевые узлы по имени тега, полностью игнорируя лишние поля.
Рассмотрим пример чтения данных:
Чтение = Новый ЧтениеXML;
Чтение.ОткрытьФайл("C:\exchange\sales.xml");
Построитель = Новый ПостроительDOM;
Документ = Построитель.Прочитать(Чтение);
Чтение.Закрыть();
// Получаем коллекцию всех узлов <sale>
СписокПродаж = Документ.ПолучитьЭлементыПоИмени("sale");
Для Каждого УзелПродажи Из СписокПродаж Цикл
Сумма = "";
Номер = "";
// Ищем только требуемые дочерние узлы
УзлыСуммы = УзелПродажи.ПолучитьЭлементыПоИмени("sum");
Если УзлыСуммы.Количество() > 0 Тогда
Сумма = УзлыСуммы.Получить(0).ТекстовоеСодержимое;
КонецЕсли;
УзлыНомера = УзелПродажи.ПолучитьЭлементыПоИмени("docNumber");
Если УзлыНомера.Количество() > 0 Тогда
Номер = УзлыНомера.Получить(0).ТекстовоеСодержимое;
КонецЕсли;
// Новые и сторонние узлы внутри <sale> будут автоматически проигнорированы
ОбработатьЗапись(Номер, Сумма);
КонецЦикла;
Если в конфигурации вся архитектура жестко завязана на вызов ФабрикаXDTO.ПрочитатьXML() и переписывать код чтения нецелесообразно, можно предварительно очистить входящий XML-файл от лишних тегов с помощью объекта ПреобразованиеXSL. После трансформации в документ попадут только те поля, которые строго описаны в XDTO-пакете.