Как определить имя некорректного XML-тега при ошибке чтения через Фабрику XDTO в 1С?

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

При интеграции с внешними системами через XML и схему XSD часто используется стандартный механизм десериализации платформы 1С: ФабрикаXDTO.ПрочитатьXML(). Этот метод выполняет строгую проверку входящих данных на соответствие типам схемы. Однако если во входящем файле передано некорректное значение (например, строка в целочисленном поле), разработчик сталкивается со специфичным поведением платформы.

Посмотрим на типичный фрагмент кода чтения XML:


Попытка
    нОбъектXDTO = ФабрикаXDTO.ПрочитатьXML(лЧтение, лТип);
Исключение
    ЗаписатьЛог(ОписаниеОшибки());
КонецПопытки;

При наличии ошибки платформа прерывает чтение и формирует исключение следующего вида: Ошибка проверки данных XDTO: Значение: 'тест1' не соответствует простому типу: {http://www.w3.org/2001/XMLSchema}integer. В тексте ошибки и объекте ИнформацияОбОшибке() выводится само ошибочное значение и целевой тип XSD, однако имя некорректного тега или свойства XDTO в явном виде отсутствует. Разберем причины такого поведения и способы точной локализации сбойного поля.

Особенности работы Фабрики XDTO при валидации типов

Проанализируем, почему платформа не возвращает имя свойства при падении метода ПрочитатьXML:

  1. Принцип работы потокового парсера (Fail-Fast): Метод ФабрикаXDTO.ПрочитатьXML() работает в потоковом режиме поверх объекта ЧтениеXML. Как только парсер натыкается на лексему с неподходящим типом данных, процесс чтения немедленно прерывается. Полноценный объект XDTO в памяти еще не сформирован.
  2. Ограничение метода ФабрикаXDTO.Проверить(): Метод Проверить() предназначен для валидации уже созданного в памяти объекта ОбъектXDTO (например, для контроля количества вхождений элементов или заполненности обязательных полей). Применить его к невалидному XML невозможно, так как сам объект не создается из-за сбоя на этапе чтения типов.

Способ 1. Получение координат ошибки из объекта ЧтениеXML

В момент генерации исключения внутри метода ПрочитатьXML указатель связанного объекта ЧтениеXML останавливается ровно на том узле, где произошла ошибка. Мы можем извлечь координаты и имя элемента непосредственно в блоке Исключение до закрытия потока чтения.

Рассмотрим реализацию этого подхода:


лЧтение = Новый ЧтениеXML;
лЧтение.ОткрытьФайл(ПутьКФайлу);

Попытка
    нОбъектXDTO = ФабрикаXDTO.ПрочитатьXML(лЧтение, лТип);
Исключение
    Инфо = ИнформацияОбОшибке();
    
    // Получаем текущие координаты чтения из объекта ЧтениеXML
    НомерСтрокиОшибки = лЧтение.НомерСтроки;
    НомерПозицииОшибки = лЧтение.НомерПозиции;
    ТекущееИмяУзла = лЧтение.Имя;
    ЛокальноеИмяУзла = лЧтение.ЛокальноеИмя;
    
    СообщениеОбОшибке = СтрШаблон(
        "Ошибка XDTO в строке %1 (позиция %2), узел: '%3'. Описание: %4",
        НомерСтрокиОшибки,
        НомерПозицииОшибки,
        ?(ЗначениеЗаполнено(ЛокальноеИмяУзла), ЛокальноеИмяУзла, ТекущееИмяУзла),
        Инфо.Описание
    );
    
    ЗаписатьЛог(СообщениеОбОшибке);
    лЧтение.Закрыть();
    ВызватьИсключение СообщениеОбОшибке;
КонецПопытки;

лЧтение.Закрыть();

Обратите внимание: если курсор находится на узле типа ТипУзлаXML.Текст, свойство лЧтение.Имя может быть пустым. В этом случае точные координаты (НомерСтроки и НомерПозиции) позволяют однозначно локализовать проблемный тег в исходном файле.

Способ 2. Локализация свойства по ошибочному значению

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

Разберем логику работы:

  1. Из строки Инфо.Описание с помощью строковых функций или регулярных выражений извлекаем фрагмент между Значение: ' и ' не соответствует.
  2. Выполняем поиск первого вхождения найденной строки в XML-документе.
  3. Определяем обрамляющий XML-тег (имя открывающего тега слева от найденного значения).

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

Способ 3. Поузловое чтение с ведением стека тегов

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

Посмотрим на пример алгоритма:


СтекТегов = Новый Массив;

Пока лЧтение.Прочитать() Цикл
    Если лЧтение.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда
        СтекТегов.Добавить(лЧтение.ЛокальноеИмя);
    ИначеЕсли лЧтение.ТипУзла = ТипУзлаXML.КонецЭлемента Тогда
        Если СтекТегов.Количество() > 0 Тогда
            СтекТегов.Удалить(СтекТегов.Количество() - 1);
        КонецЕсли;
    КонецЕсли;
КонецЦикла;

Если внутри итерации вызывается десериализация конкретного составного узла через ФабрикаXDTO.ПрочитатьXML(лЧтение), то при возникновении исключения текущее состояние массива СтекТегов дает полную иерархическую цепочку родительских элементов.

Способ 4. Проверка пространств имен в XML-документе

Нередко ошибки несоответствия типов возникают из-за некорректно указанного пространства имен в корневом теге XML. Если фабрика XDTO ожидает типы из целевого пространства имен (URI), а в файле пространство имен отсутствует или указано с опечаткой, парсер может пытаться сопоставить данные с некорректными типами по умолчанию.

Убедимся, что корневой элемент документа содержит корректное объявление пространства имен:


// Пример добавления пространства имен в заголовок XML при необходимости
Если СтрНайти(ТекстXML, "xmlns=") = 0 Тогда
    ТекстXML = СтрЗаменить(ТекстXML, "<PERS>", "<PERS xmlns=""uri:TFOMS_PK"">");
КонецЕсли;

Способ 5. Пакетная валидация файлов на сервере Linux через утилиту xmllint

Если требуется обрабатывать большой поток файлов в автоматическом режиме на сервере под управлением ОС Linux и получать полный список всех некорректных тегов (а не только первого сбойного), оптимально выполнять предварительную валидацию с помощью системной утилиты xmllint.

Разберем схему работы:

  1. Схема XSD сохраняется на сервере в отдельный файл.
  2. С помощью метода ЗапуститьПриложение() вызывается команда: xmllint --noout --schema /path/to/schema.xsd /path/to/input.xml
  3. Утилита xmllint выполняет полную проверку файла по XSD за один проход и возвращает в стандартный поток ошибок подробный отчет со всеми строками, именами тегов и несоответствиями типов.
  4. Если код возврата утилиты равен нулю, файл гарантированно валиден и успешно прочитается методом ФабрикаXDTO.ПрочитатьXML() без генерации исключений.
← На главную