Как преобразовать anyType в строку при чтении XML через Фабрику XDTO?

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

При работе с интеграциями часто возникает ситуация, когда мы получаем XML-данные, структура которых нам известна, но не имеет строгого описания в виде XSD-схемы. В таких случаях программисты 1С часто используют глобальную ФабрикаXDTO для быстрого разбора файла. Однако здесь кроется неприятный нюанс: если тег в XML пустой (например, <Email></Email>) или помечен как пустой через атрибут xsi:nil="true", система 1С не может однозначно определить его тип и создает ОбъектXDTO с типом anyType. Вместо ожидаемой пустой строки мы получаем объект, который нельзя просто так сравнить или записать в реквизит справочника.

В этой статье мы подробно разберем, почему так происходит, и рассмотрим несколько эффективных способов решения этой задачи, от программной обработки до настройки пакетов XDTO.

Почему Фабрика XDTO создает объект вместо строки?

Разберем ситуацию на примере. Когда мы используем метод Фабрика.ПрочитатьXML(ЧтениеXML) без указания конкретного типа, механизм XDTO пытается "угадать" тип данных. Если внутри тега есть текст, он часто интерпретируется как строка. Если же тег пуст, то по правилам XML Schema это может быть как пустая строка, так и сложный объект, у которого просто не заполнены вложенные элементы. Не имея схемы (XSD), 1С выбирает наиболее универсальный вариант — anyType, что в объектной модели 1С превращается в ОбъектXDTO.

Проблема усугубляется тем, что стандартная функция ЗначениеЗаполнено() возвращает Истина для такого объекта, так как сам объект в памяти существует, хоть он и пуст внутри. Проанализируем способы, которые помогут нам получить именно строковое значение.

Решение 1: Программная обработка результата (универсальный помощник)

Если мы не хотим или не можем создавать пакеты 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
КонецЦикла;

Решение 2: Использование пакетов XDTO (Правильный подход)

Как совершенно справедливо заметили участники обсуждения на форуме, наиболее надежный способ — это "засучить рукава" и описать структуру XML в метаданных. Когда у ФабрикаXDTO есть информация о типах, она точно знает, что Email — это string, и преобразует пустой тег в пустую строку автоматически.

Разберем по шагам, как это сделать:

  1. Создайте новый Пакет XDTO в конфигураторе.
  2. Укажите URI пространства имен (он должен совпадать с xmlns в вашем XML, если он есть).
  3. Опишите тип объекта (например, RowType) и добавьте в него свойства (Name, Email, Phone).
  4. Для каждого свойства укажите тип из пространства имен XML-схемы: {http://www.w3.org/2001/XMLSchema}string.

Важный момент: если вы используете созданный пакет, чтение XML нужно выполнять с указанием типа:


ТипОбъекта = ФабрикаXDTO.Тип("http://my-namespace.com", "RowType");
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.УстановитьСтроку(XMLДанные);
// Переходим к нужному узлу
ЧтениеXML.Прочитать(); 
Результат = ФабрикаXDTO.ПрочитатьXML(ЧтениеXML, ТипОбъекта);

В этом случае свойство Результат.Email всегда будет иметь тип Строка, независимо от того, пустой тег в XML или нет.

Решение 3: Использование DOM как альтернатива

Если ваша задача — просто получить текст из тегов, и вам не нужна сложная валидация типов XDTO, проанализируем использование объектной модели документа (DOM). Это "классический" способ работы с XML, который всегда возвращает содержимое узлов в виде строк.

Посмотрим на пример кода:


ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.УстановитьСтроку(XMLДанные);
ПостроительDOM = Новый ПостроительDOM;
ДокументDOM = ПостроительDOM.Прочитать(ЧтениеXML);

СписокСтрок = ДокументDOM.ПолучитьЭлементыПоИмени("row");
Для Каждого УзелRow Из СписокСтрок Цикл
    // Текстовое содержимое всегда строка, даже если тег пуст
    Почта = УзелRow.ПолучитьЭлементыПоИмени("Email").Получить(0).ТекстовоеСодержимое;
КонецЦикла;

Преимущество: DOM не пытается интерпретировать типы данных. Для него всё — текст. Это избавляет от необходимости проверять, является ли значение объектом anyType.

Решение 4: Прямое чтение через ЧтениеXML

Выясним, можно ли обойтись еще более простыми средствами. Если структура XML плоская и простая (как в примере автора темы), использование Фабрики XDTO может быть избыточным. При использовании объекта ЧтениеXML напрямую, мы контролируем каждый шаг парсинга.


ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.УстановитьСтроку(XMLДанные);

Пока ЧтениеXML.Прочитать() Цикл
    Если ЧтениеXML.ТипУзла = ТипУзлаXML.НачалоЭлемента И ЧтениеXML.Имя = "Email" Тогда
        // Читаем содержимое. Если тег пустой, метод Прочитать() сразу 
        // перейдет к концу элемента, и мы получим пустую строку.
        ЧтениеXML.Прочитать();
        Если ЧтениеXML.ТипУзла = ТипУзлаXML.Текст Тогда
            ЗначениеEmail = ЧтениеXML.Значение;
        Иначе
            ЗначениеEmail = "";
        КонецЕсли;
    КонецЕсли;
КонецЦикла;

Подведем итоги

Выбор метода зависит от сложности вашей задачи:

Помните, что anyType — это не ошибка системы, а способ 1С сохранить структуру данных в условиях неопределенности. Правильная настройка фабрики или корректная обработка типов на входе сделает ваш код надежным и устойчивым к изменениям во входящих XML-файлах.

← На главную