Чтение XML-файлов — одна из самых частых задач при организации обмена данными между 1С и внешними системами. Однако, когда структура файла становится сложнее «плоского» списка, разработчики часто сталкиваются с трудностями при попытке извлечь данные из вложенных узлов (например, строк табличной части внутри документа). Рассмотрим, как правильно организовать алгоритм обхода XML, проанализируем ошибки стандартного потокового чтения и разберем более современные подходы, такие как DOM и XDTO.
Разберем ситуацию, с которой столкнулся автор темы. При использовании объекта ЧтениеXML программа читает файл последовательно, как поток. Основная ошибка заключалась в попытке вызвать метод ПрочитатьАтрибут() внутри цикла, когда курсор уже стоял на начале элемента. Метод ПрочитатьАтрибут() предназначен для перебора атрибутов текущего узла, а не для перехода к вложенным дочерним узлам.
Для корректной работы с вложенными структурами через ЧтениеXML нам необходимо четко отслеживать типы узлов: НачалоЭлемента и КонецЭлемента. Посмотрим на правильную логику реализации этого процесса:
// Открываем файл для чтения
Чтение = Новый ЧтениеXML;
Чтение.ОткрытьФайл(ПутьКФайлу);
МассивДокументов = Новый Массив;
Пока Чтение.Прочитать() Цикл
// Проверяем начало основного узла документа
Если Чтение.ТипУзла = ТипУзлаXML.НачалоЭлемента И Чтение.Имя = "ПоступлениеДок" Тогда
ДанныеДокумента = Новый Структура;
ДанныеДокумента.Вставить("Номер", Чтение.ЗначениеАтрибута("Номер"));
ДанныеДокумента.Вставить("Дата", Чтение.ЗначениеАтрибута("Дата"));
// Создаем массив для хранения строк табличной части
МассивТЧ = Новый Массив;
// Вложенный цикл для поиска и обхода узла ТЧТовары
Пока Чтение.Прочитать() Цикл
// Если нашли начало строки товара
Если Чтение.ТипУзла = ТипУзлаXML.НачалоЭлемента И Чтение.Имя = "СтрокаТовара" Тогда
СтруктураСтроки = Новый Структура;
СтруктураСтроки.Вставить("Номенклатура", Чтение.ЗначениеАтрибута("Номенклатура"));
СтруктураСтроки.Вставить("Количество", Чтение.ЗначениеАтрибута("Количество"));
СтруктураСтроки.Вставить("Цена", Чтение.ЗначениеАтрибута("Цена"));
МассивТЧ.Добавить(СтруктураСтроки);
// Важно: проверяем окончание родительского узла документа
ИначеЕсли Чтение.ТипУзла = ТипУзлаXML.КонецЭлемента И Чтение.Имя = "ПоступлениеДок" Тогда
Прервать; // Выходим из внутреннего цикла, так как документ закончился
КонецЕсли;
КонецЦикла;
ДанныеДокумента.Вставить("Товары", МассивТЧ);
МассивДокументов.Добавить(ДанныеДокумента);
КонецЕсли;
КонецЦикла;
Чтение.Закрыть();
Важный нюанс: Потоковое чтение максимально эффективно с точки зрения потребления памяти, так как 1С не загружает весь XML-файл целиком. Это критично при работе с файлами объемом в сотни мегабайт.
Если файл имеет средний размер (до 10-20 Мб), гораздо удобнее использовать Объектную Модель Документа (DOM). В этом случае 1С строит в памяти дерево документа, по которому мы можем перемещаться в любом направлении.
Проанализируем шаги работы с DOM через ПостроительDOM:
ЧтениеXML.ПостроительDOM создает из него объект ДокументDOM.ПолучитьЭлементыПоИмени или обходим дерево вручную.Рассмотрим пример кода:
Чтение = Новый ЧтениеXML;
Чтение.ОткрытьФайл(ПутьКФайлу);
Построитель = Новый ПостроительDOM;
ДокументDOM = Построитель.Прочитать(Чтение);
// Получаем все узлы документов
СписокДокументов = ДокументDOM.ПолучитьЭлементыПоИмени("ПоступлениеДок");
Для каждого УзелДокумента Из СписокДокументов Цикл
// Читаем атрибуты через карту атрибутов
Атрибуты = УзелДокумента.Атрибуты;
НомерДок = Атрибуты.ПолучитьИмя("Номер").Значение;
// Ищем вложенный узел табличной части
УзлыТЧ = УзелДокумента.ПолучитьЭлементыПоИмени("ТЧТовары");
Если УзлыТЧ.Количество() > 0 Тогда
// Перебираем строки внутри первого найденного узла ТЧ
СтрокиТоваров = УзлыТЧ[0].ДочерниеУзлы;
Для каждого СтрокаТЧ Из СтрокиТоваров Цикл
Если СтрокаТЧ.ТипУзла = ТипУзлаDOM.Элемент Тогда
// Обработка полей товара
ИмяТовара = СтрокаТЧ.Атрибуты.ПолучитьИмя("Номенклатура").Значение;
КонецЕсли;
КонецЦикла;
КонецЕсли;
КонецЦикла;
Чтение.Закрыть();
Метод DOM значительно упрощает доступ к данным, так как нам не нужно следить за последовательностью чтения. Мы можем обратиться к любому свойству узла в любой момент. Для быстрого поиска нужных элементов в дереве DOM можно использовать методику применения языка запросов XPath в 1С.
Как было отмечено в обсуждении на форуме, использование XDTO (XML Data Transfer Objects) является наиболее мощным и «правильным» способом для 1С. Этот механизм позволяет превратить XML-файл в объектную структуру 1С (аналог набора вложенных структур и списков) автоматически. Для освоения базовых принципов работы с механизмом подойдёт руководство по основам XDTO в 1С.
Если у вас есть XSD-схема (файл описания структуры), вы можете импортировать её в конфигурацию как Пакет XDTO. Если схемы нет, 1С может попытаться прочитать XML в каноническом виде.
Рассмотрим, как это выглядит в коде, если мы используем типизацию:
Чтение = Новый ЧтениеXML;
Чтение.ОткрытьФайл(ПутьКФайлу);
// Читаем данные напрямую в объект XDTO
// Предположим, у нас есть настроенная фабрика или мы читаем неопределенный тип
ОбъектДанных = ФабрикаXDTO.ПрочитатьXML(Чтение);
// Теперь мы можем обращаться к данным через точку, как к обычным реквизитам
Для каждого ДокументИзФайла Из ОбъектДанных.ПоступлениеДок Цикл
Сообщить("Документ № " + ДокументИзФайла.Номер);
// Табличная часть теперь — это просто коллекция (СписокXDTO)
Для каждого СтрокаТовара Из ДокументИзФайла.ТЧТовары.Строка Цикл
Сообщить(" - Товар: " + СтрокаТовара.Номенклатура);
Сообщить(" - Сумма: " + СтрокаТовара.Сумма);
КонецЦикла;
КонецЦикла;
Чтение.Закрыть();
Преимущества XDTO очевидны: строгая типизация (числа становятся числами, даты — датами), отсутствие необходимости ручного перебора узлов и высокая скорость разработки.
Выясним причину выбора того или иного метода в зависимости от условий задачи:
При чтении вложенных узлов обязательно учитывайте возможность отсутствия данных. Проанализируем ситуацию: если в XML-файле отсутствует узел табличной части, ваш код не должен «падать» с ошибкой. Используйте проверки ЗначениеЗаполнено() или проверяйте количество дочерних узлов перед началом цикла обхода.
Также помните о Пространствах имен (Namespace). Если в корневом теге XML указаны параметры типа xmlns:ns="http://www.site.com", то при поиске узлов через DOM или при последовательном чтении нужно использовать ЛокальноеИмя или указывать URI пространства имен, иначе 1С просто «не увидит» элементы.
Таким образом, для решения задачи автора наиболее эффективным оказалось использование XDTO, что позволило абстрагироваться от низкоуровневого разбора тегов и работать с данными как с высокоуровневыми объектами платформы 1С.