При интеграции 1С с внешними информационными системами разработчики часто сталкиваются с необходимостью разбора XML-файлов. Одним из наиболее удобных инструментов для этого является Фабрика XDTO. Однако при работе с нетипизированными XML-документами «в лоб» возникает классическая проблема: если узел XML одновременно содержит и атрибуты (например, дату создания), и текстовое значение внутри самого тега (например, код маркировки), стандартное обращение через точку к свойству объекта XDTO возвращает только атрибуты. Текстовое содержимое тега при этом кажется недоступным.
В этой статье мы подробно разберем, почему возникает эта проблема, детально проанализируем внутреннее устройство XDTO в 1С, изучим быстрый способ обхода ограничения с помощью последовательностей, предотвратим опасные ошибки парсинга единичных элементов и рассмотрим наиболее надежные архитектурные решения — создание собственного пакета XDTO и использование объектной модели DOM.
Для того чтобы понять, почему 1С не показывает текстовое значение тега напрямую, нам необходимо заглянуть под капот механизма XDTO. Когда мы читаем XML-файл с помощью глобальной фабрики XDTO без предварительного указания конкретной XSD-схемы (то есть динамически), платформа создает объекты типов по умолчанию. Для узлов с атрибутами и текстом создается тип anyType пространства имен http://www.w3.org/2001/XMLSchema.
С точки зрения стандартов XML, такой узел представляет собой элемент со смешанным содержимым (mixed content). Платформа 1С обрабатывает его следующим образом:
InsertDate) преобразуются в именованные свойства созданного объекта ОбъектXDTO. К ним можно легко обратиться напрямую: КодXDTO.InsertDate.ПоследовательностьXDTO). Она хранит элементы данных в порядке их следования в XML-документе.Попытка прочитать значение тега напрямую через обращение к свойству или через метод Получить() не принесет результата, так как это свойство просто не спроецировано в структуру объекта как стандартное поле.
Если вам нужно быстро решить задачу «здесь и сейчас», не создавая при этом полноценных схем XDTO в конфигурации, вы можете извлечь текст напрямую из последовательности объекта. Для этого у любого объекта XDTO существует метод Последовательность(), который возвращает коллекцию элементов. У этой коллекции есть метод ПолучитьТекст().
Рассмотрим пример кода, который позволяет получить текст первого элемента:
// Получаем текст из последовательности конкретного объекта XDTO
ТекстКода = ОбъектКода.Последовательность().ПолучитьТекст(0);
В данном случае индекс 0 указывает на то, что мы берем самый первый текстовый блок внутри тега. Метод работает стабильно, если структура XML строго фиксирована и внутри тега гарантированно нет лишних переносов строк, комментариев или вложенных подтегов, которые могли бы сместить индекс текста в последовательности.
При использовании быстрого метода чтения через последовательности программисты часто совершают критическую ошибку, которая приводит к падению системы в реальной эксплуатации. Эта проблема называется ловушкой единичного элемента.
Проанализируем поведение платформы 1С при чтении списков XML-элементов через фабрику XDTO без явной схемы:
<Code>), фабрика XDTO автоматически преобразует их в коллекцию — СписокXDTO. В этом случае обращение по индексу вида обХДТО.Code[0] отработает корректно.<Code>, платформа 1С не станет создавать список. Она прочитает этот элемент как одиночный ОбъектXDTO. Попытка обратиться к нему как к массиву (обХДТО.Code[0]) вызовет критическую ошибку выполнения: «Получение элемента по индексу для значения не разрешено».Чтобы ваш код работал надежно при любом количестве элементов во входящем XML-файле, необходимо всегда выполнять проверку типа возвращаемого значения. Посмотрим на пример универсального и безопасного алгоритма:
// Проверяем, существует ли свойство "Code" в прочитанном объекте
Если обХДТО.Свойства().Получить("Code") <> Неопределено Тогда
ЗначениеCode = обХДТО.Code;
Если ТипЗнч(ЗначениеCode) = Тип("СписокXDTO") Тогда
// Сценарий 1: В файле найдено несколько кодов маркировки (это список)
Для Каждого Элемент Из ЗначениеCode Цикл
Если Элемент.Последовательность().Количество() > 0 Тогда
ТекстКода = Элемент.Последовательность().ПолучитьТекст(0);
// Выполняем обработку полученного текста кода
ЗаписатьКодВБазу(ТекстКода, Элемент.InsertDate);
КонецЕсли;
КонецЦикла;
ИначеЕсли ТипЗнч(ЗначениеCode) = Тип("ОбъектXDTO") Тогда
// Сценарий 2: В файле присутствует только один код (это одиночный объект)
Если ЗначениеCode.Последовательность().Количество() > 0 Тогда
ТекстКода = ЗначениеCode.Последовательность().ПолучитьТекст(0);
// Выполняем обработку единственного кода
ЗаписатьКодВБазу(ТекстКода, ЗначениеCode.InsertDate);
КонецЕсли;
КонецЕсли;
КонецЕсли;
Этот подход гарантирует стабильность работы интеграции независимо от объема передаваемых данных.
Обращение к тексту по жесткому индексу ПолучитьТекст(0) не всегда безопасно. В реальных XML-файлах перед полезным текстом могут находиться невидимые символы форматирования, пробелы, знаки переноса строк или XML-комментарии. В таких случаях индекс полезного текста может оказаться отличным от нуля.
Рассмотрим более надежный способ обхода последовательности. Мы будем программно перебирать все элементы последовательности объекта XDTO и искать именно текстовые узлы:
Последовательность = ЭлементКода.Последовательность();
ТекстКода = "";
Для Индекс = 0 По Последовательность.Количество() - 1 Цикл
// Если метод ПолучитьСвойство возвращает Неопределено,
// значит данный элемент последовательности является простым текстом, а не дочерним свойством
Если Последовательность.ПолучитьСвойство(Индекс) = Неопределено Тогда
ТекстКода = СокрЛП(Последовательность.ПолучитьТекст(Индекс));
Если Не ПустаяСтрока(ТекстКода) Тогда
// Нашли полезное текстовое содержимое, выходим из цикла
Прервать;
КонецЕсли;
КонецЕсли;
КонецЦикла;
Использование этого алгоритма полностью защищает ваш код от сдвигов структуры внутри тегов XML.
Использование последовательностей — отличный обходной путь, однако с точки зрения архитектуры 1С правильным и наиболее производительным решением является типизация данных. Для этого необходимо импортировать или вручную создать схему XML (пакет XDTO) в вашей конфигурации.
Разберем по шагам, как правильно настроить схему XDTO для чтения смешанного содержимого:
CodeType).InsertDate, укажите тип (например, dateTime) и обязательно установите свойство Форма представления в значение Атрибут.__content, но вы можете назвать его по своему усмотрению, например, Значение). Установите для этого свойства Форма представления в значение Текст.После того как схема создана и привязана к чтению XML-документа, платформа 1С автоматически сопоставит текстовое содержимое тега с вашим свойством __content. Код разбора XML станет лаконичным, легко читаемым и быстрым:
// Пример чтения XML с использованием типизированного пакета XDTO
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.ОткрытьФайл(ПутьКФайлу);
// Читаем объект с указанием типа из нашего пакета XDTO
ОбъектМаркировки = ФабрикаXDTO.ПрочитатьXML(ЧтениеXML, ФабрикаXDTO.Тип("http://www.yourdomain.ru/marks", "MarkCodes"));
Для Каждого Элемент Из ОбъектМаркировки.Code Цикл
// Чтение атрибута и значения теперь выполняется максимально просто "через точку"
ДатаСоздания = Элемент.InsertDate;
ТекстКода = Элемент.__content; // или имя, которое вы задали в схеме
// Обработка данных...
КонецЦикла;
ЧтениеXML.Закрыть();
Главное преимущество этого способа — отсутствие необходимости писать сложные разветвленные проверки типов и вручную обходить последовательности. Платформа 1С берет всю рутину по парсингу структуры на себя.
Если создание пакета XDTO по каким-то причинам невозможно (например, схема часто меняется внешним поставщиком услуг, а решение должно быть максимально гибким), а писать громоздкий код обработки последовательностей XDTO не хочется, можно полностью отказаться от XDTO в пользу технологии DOM (Document Object Model).
Объектная модель документов DOM загружает весь XML-файл в память в виде дерева объектов. Она лишена недостатка XDTO, связанного с разделением на списки и одиночные объекты, и позволяет одинаково просто обращаться как к атрибутам, так и к текстовому наполнению любых узлов.
Проанализируем пример реализации чтения XML через DOM:
// Открываем файл для чтения
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.ОткрытьФайл(ПутьКФайлу);
// Строим объектную модель документа (DOM)
ПостроительDOM = Новый ПостроительDOM;
ДокументDOM = ПостроительDOM.Прочитать(ЧтениеXML);
// Получаем все элементы с именем тега "Code" независимо от уровня их вложенности
СписокЭлементов = ДокументDOM.ПолучитьЭлементыПоИмени("Code");
Для Каждого ЭлементDOM Из СписокЭлементов Цикл
// Извлечение значения атрибута "InsertDate"
ДатаВставки = ЭлементDOM.ПолучитьАтрибут("InsertDate");
// Извлечение текстового содержимого, находящегося внутри тега
ТекстКода = СокрЛП(ЭлементDOM.ТекстовоеСодержимое);
// Выполняем дальнейшие операции с данными
ОбработатьДанныеDOM(ТекстКода, ДатаВставки);
КонецЦикла;
// Обязательно закрываем файл чтения
ЧтениеXML.Закрыть();
Этот способ крайне надежен, лаконичен и отлично подходит для файлов небольшого и среднего размера. Однако стоит учитывать, что DOM загружает весь файл в оперативную память целиком, поэтому для обработки очень больших файлов (от 100 Мегабайт и выше) лучше использовать последовательное чтение через ЧтениеXML или оптимизированные пакеты XDTO.
Для того чтобы выбрать наиболее подходящий метод для вашего проекта, сопоставим рассмотренные подходы по ключевым критериям:
__content): Самый производительный и красивый с точки зрения архитектуры подход. Код получается чистым и понятным. Идеально для постоянных интеграционных процессов с фиксированным форматом обмена. Минус: требует внесения изменений в метаданные (создания XDTO-пакета).Рекомендуем всегда отдавать предпочтение созданию полноценных пакетов XDTO. Это гарантирует высокую скорость выполнения операций, строгую типизацию данных и упрощает дальнейшую поддержку программного кода вашими коллегами. Для предварительного контроля входящих файлов на соответствие правилам схемы можно использовать инструмент для проверки валидности XML-файлов по схеме XSD или пакету XDTO.