При интеграции 1С с внешними системами или государственными информационными сервисами разработчики часто сталкиваются с форматом XML, где данные передаются не только внутри тегов (элементов), но и в виде атрибутов. Ситуация осложняется, когда структура файла заранее неизвестна или содержит "смешанный" контент. В этой статье мы разберем основные способы извлечения данных из таких файлов, проанализируем ошибки при использовании объектной модели DOM и выясним, какой метод лучше подходит для разных задач.
Это самый быстрый и экономичный с точки зрения памяти способ. Мы последовательно перебираем узлы файла от начала к концу. Если нам встречаются атрибуты, мы должны обработать их сразу после обнаружения начала элемента. Рассмотрим алгоритм действий на примере разбора сведений об алкогольной продукции. Для автоматической загрузки структуры в табличный вид можно воспользоваться обработкой для быстрого преобразования XML в таблицу значений.
Для реализации этого метода воспользуемся циклом Пока XML.Прочитать(). Когда указатель попадает на тип узла ТипУзлаXML.НачалоЭлемента, мы можем прочитать все атрибуты данного тега во вложенном цикле Пока XML.ПрочитатьАтрибут().
Разберем пример кода:
Процедура ЗагрузитьСодержимоеXML(ИмяФайла)
Если ПустаяСтрока(ИмяФайла) Тогда
Возврат;
КонецЕсли;
XML = Новый ЧтениеXML;
XML.ОткрытьФайл(ИмяФайла);
Пока XML.Прочитать() Цикл
// Проверяем, что мы зашли внутрь нового тега
Если XML.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда
// Если мы знаем имя атрибута, можем получить его напрямую
ИНН = XML.ПолучитьАтрибут("INN");
// Либо перебираем все доступные атрибуты в цикле
Пока XML.ПрочитатьАтрибут() Цикл
Если XML.Имя = "П000000000003" Тогда
ВидПродукции = Справочники.ВидыАлкогольнойПродукции.НайтиПоКоду(XML.Значение);
ИначеЕсли XML.Имя = "NameOrg" Тогда
ПроизводительНаименование = XML.Значение;
ИначеЕсли XML.Имя = "KPP" Тогда
КПП = XML.Значение;
КонецЕсли;
КонецЦикла;
КонецЕсли;
// Обработка текстового содержимого между тегами
Если XML.ТипУзла = ТипУзлаXML.Текст Тогда
ТекстовоеЗначение = XML.Значение;
КонецЕсли;
// Фиксация данных при закрытии тега
Если XML.ТипУзла = ТипУзлаXML.КонецЭлемента И XML.Имя = "Продукция" Тогда
// Здесь записываем накопленные данные в таблицу или справочник
НовСтрока = ТаблицаДанных.Добавить();
НовСтрока.ИНН = ИНН;
НовСтрока.Вид = ВидПродукции;
КонецЕсли;
КонецЦикла;
XML.Закрыть();
КонецПроцедуры
Важный нюанс: метод ПрочитатьАтрибут() изменяет состояние объекта чтения. После того как вы перебрали все атрибуты, вы не сможете вернуться к началу текущего элемента. Поэтому все важные данные из атрибутов нужно сохранять в переменные сразу.
Иногда структура XML слишком сложная, и последовательное чтение превращается в громоздкое нагромождение условий Если...ИначеЕсли. В таких случаях удобнее использовать ПостроительDOM, который считывает весь файл в память и представляет его в виде дерева объектов.
Проанализируем типичную ситуацию: вы загрузили документ и пытаетесь перебрать дочерние узлы, но вместо ожидаемых имен тегов видите странное значение #text. Это происходит потому, что в XML-файле переводы строк и пробелы между тегами считаются текстовыми узлами. Рассмотрим, как правильно обходить дерево элементов, игнорируя лишний шум.
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.ОткрытьФайл(ПутьКФайлу);
Построитель = Новый ПостроительDOM;
ОтветДокумент = Построитель.Прочитать(ЧтениеXML);
// Корень документа - ЭлементДокумента
КорневойУзел = ОтветДокумент.ЭлементДокумента;
Для Каждого Узел Из КорневойУзел.ДочерниеУзлы Цикл
// Обязательно проверяем тип узла!
Если Узел.ТипУзла <> ТипУзлаDOM.Элемент Тогда
Продолжить;
КонецЕсли;
Сообщить("Имя тега: " + Узел.ИмяУзла);
// Работа с атрибутами через коллекцию Атрибуты
АтрибутError = Узел.Атрибуты.ПолучитьИменованныйЭлемент("ErrorName");
Если АтрибутError <> Неопределено Тогда
Сообщить("Значение атрибута: " + АтрибутError.Значение);
КонецЕсли;
КонецЦикла;
Если файл имеет неопределенную вложенность, использование простых циклов Для каждого будет неэффективным (придется вкладывать цикл в цикл для каждого уровня). Выясним причину, по которой начинающие разработчики часто "теряют" данные: они разбирают только верхний уровень. Для полного разбора необходимо использовать рекурсию.
Рассмотрим пример функции для рекурсивного обхода:
Процедура ОбойтиУзлыРекурсивно(ТекущийУзел)
Для Каждого ПодчиненныйУзел Из ТекущийУзел.ДочерниеУзлы Цикл
Если ПодчиненныйУзел.ТипУзла = ТипУзлаDOM.Элемент Тогда
// Обрабатываем атрибуты текущего узла
Для Каждого Атрибут Из ПодчиненныйУзел.Атрибуты Цикл
Сообщить("Элемент: " + ПодчиненныйУзел.ИмяУзла + ", Атрибут: " + Атрибут.ИмяУзла + " = " + Атрибут.Значение);
КонецЦикла;
// Рекурсивный вызов для вложенных уровней
ОбойтиУзлыРекурсивно(ПодчиненныйУзел);
КонецЕсли;
КонецЦикла;
КонецПроцедуры
ВычислитьВыражениеXPath(). Например, путь //Продукция/@INN сразу вернет значение ИНН из всех узлов "Продукция".#text при чтении, установите свойство ПараметрыЧтенияXML.ИгнорироватьПробелы = Истина перед открытием файла в ЧтениеXML.Объект.ИНН.HTTPСоединение, всегда проверяйте кодировку. Если в ResponseText попадают лишние символы до объявления XML, ПостроительDOM выдаст ошибку. В таком случае сначала очистите строку функцией СократитьЛП() или удалите невидимые символы в начале.Таким образом, выбор метода зависит от ваших задач. Если файл огромен (например, выгрузка из ЕГАИС на 100 Мб) — используйте ЧтениеXML. Если файл небольшой, но со сложной вложенностью и вам нужно "прыгать" по веткам вверх-вниз — используйте DOM и XPath. Понимание разницы между типами узлов и умение работать с коллекцией атрибутов позволит вам разобрать XML любой сложности. Для практического освоения выборки данных по путям подойдёт руководство по использованию XPath в 1С.