При работе с интеграциями часто возникает ситуация, когда мы получаем XML-данные, структура которых нам известна, но не имеет строгого описания в виде XSD-схемы. В таких случаях программисты 1С часто используют глобальную ФабрикаXDTO для быстрого разбора файла. Однако здесь кроется неприятный нюанс: если тег в XML пустой (например, <Email></Email>) или помечен как пустой через атрибут xsi:nil="true", система 1С не может однозначно определить его тип и создает ОбъектXDTO с типом anyType. Вместо ожидаемой пустой строки мы получаем объект, который нельзя просто так сравнить или записать в реквизит справочника.
В этой статье мы подробно разберем, почему так происходит, и рассмотрим несколько эффективных способов решения этой задачи, от программной обработки до настройки пакетов XDTO.
Разберем ситуацию на примере. Когда мы используем метод Фабрика.ПрочитатьXML(ЧтениеXML) без указания конкретного типа, механизм XDTO пытается "угадать" тип данных. Если внутри тега есть текст, он часто интерпретируется как строка. Если же тег пуст, то по правилам XML Schema это может быть как пустая строка, так и сложный объект, у которого просто не заполнены вложенные элементы. Не имея схемы (XSD), 1С выбирает наиболее универсальный вариант — anyType, что в объектной модели 1С превращается в ОбъектXDTO.
Проблема усугубляется тем, что стандартная функция ЗначениеЗаполнено() возвращает Истина для такого объекта, так как сам объект в памяти существует, хоть он и пуст внутри. Проанализируем способы, которые помогут нам получить именно строковое значение.
Если мы не хотим или не можем создавать пакеты XDTO в конфигурации, нам придется написать небольшую функцию-обертку, которая будет проверять тип полученного значения. Если значение является объектом XDTO типа anyType, мы будем принудительно возвращать пустую строку.
Рассмотрим пример реализации такой функции:
Функция ПолучитьЗначениеКакСтроку(ЗначениеXDTO)
// Проверяем, является ли значение объектом XDTO
Если ТипЗнч(ЗначениеXDTO) = Тип("ОбъектXDTO") Тогда
// Если это объект, проверяем его текстовое содержимое
// Для manyTypes без вложенных элементов это обычно пустая строка
Попытка
Возврат СокрЛП(ЗначениеXDTO.Последовательность().ПолучитьТекст(0));
Исключение
// Если последовательности нет или она пуста, значит данных нет
Возврат "";
КонецПопытки;
ИначеЕсли ЗначениеXDTO = Неопределено Тогда
Возврат "";
Иначе
// Если это уже простой тип (Строка, Дата, Число) - возвращаем как есть
Возврат Строка(ЗначениеXDTO);
КонецЕсли;
КонецФункции
Использование этой функции в вашем коде позволит избежать ошибок при записи данных:
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.УстановитьСтроку(XMLДанные);
РезультатXDTO = ФабрикаXDTO.ПрочитатьXML(ЧтениеXML);
Для Каждого СтрокаДанных Из РезультатXDTO.row Цикл
ФИО = ПолучитьЗначениеКакСтроку(СтрокаДанных.Name);
ЭлектроннаяПочта = ПолучитьЗначениеКакСтроку(СтрокаДанных.Email); // Здесь будет "" вместо ОбъектаXDTO
КонецЦикла;
Как совершенно справедливо заметили участники обсуждения на форуме, наиболее надежный способ — это "засучить рукава" и описать структуру XML в метаданных. Когда у ФабрикаXDTO есть информация о типах, она точно знает, что Email — это string, и преобразует пустой тег в пустую строку автоматически.
Разберем по шагам, как это сделать:
URI пространства имен (он должен совпадать с xmlns в вашем XML, если он есть).RowType) и добавьте в него свойства (Name, Email, Phone).{http://www.w3.org/2001/XMLSchema}string.Важный момент: если вы используете созданный пакет, чтение XML нужно выполнять с указанием типа:
ТипОбъекта = ФабрикаXDTO.Тип("http://my-namespace.com", "RowType");
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.УстановитьСтроку(XMLДанные);
// Переходим к нужному узлу
ЧтениеXML.Прочитать();
Результат = ФабрикаXDTO.ПрочитатьXML(ЧтениеXML, ТипОбъекта);
В этом случае свойство Результат.Email всегда будет иметь тип Строка, независимо от того, пустой тег в XML или нет.
Если ваша задача — просто получить текст из тегов, и вам не нужна сложная валидация типов XDTO, проанализируем использование объектной модели документа (DOM). Это "классический" способ работы с XML, который всегда возвращает содержимое узлов в виде строк.
Посмотрим на пример кода:
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.УстановитьСтроку(XMLДанные);
ПостроительDOM = Новый ПостроительDOM;
ДокументDOM = ПостроительDOM.Прочитать(ЧтениеXML);
СписокСтрок = ДокументDOM.ПолучитьЭлементыПоИмени("row");
Для Каждого УзелRow Из СписокСтрок Цикл
// Текстовое содержимое всегда строка, даже если тег пуст
Почта = УзелRow.ПолучитьЭлементыПоИмени("Email").Получить(0).ТекстовоеСодержимое;
КонецЦикла;
Преимущество: DOM не пытается интерпретировать типы данных. Для него всё — текст. Это избавляет от необходимости проверять, является ли значение объектом anyType.
Выясним, можно ли обойтись еще более простыми средствами. Если структура XML плоская и простая (как в примере автора темы), использование Фабрики XDTO может быть избыточным. При использовании объекта ЧтениеXML напрямую, мы контролируем каждый шаг парсинга.
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.УстановитьСтроку(XMLДанные);
Пока ЧтениеXML.Прочитать() Цикл
Если ЧтениеXML.ТипУзла = ТипУзлаXML.НачалоЭлемента И ЧтениеXML.Имя = "Email" Тогда
// Читаем содержимое. Если тег пустой, метод Прочитать() сразу
// перейдет к концу элемента, и мы получим пустую строку.
ЧтениеXML.Прочитать();
Если ЧтениеXML.ТипУзла = ТипУзлаXML.Текст Тогда
ЗначениеEmail = ЧтениеXML.Значение;
Иначе
ЗначениеEmail = "";
КонецЕсли;
КонецЕсли;
КонецЦикла;
Выбор метода зависит от сложности вашей задачи:
ОбъектXDTO.Помните, что anyType — это не ошибка системы, а способ 1С сохранить структуру данных в условиях неопределенности. Правильная настройка фабрики или корректная обработка типов на входе сделает ваш код надежным и устойчивым к изменениям во входящих XML-файлах.