Разбор XML документа - почти все возможные способы

Публикация № 311011

Разработка - Практика программирования

XML DOM XPath

К вам пришел XML документ, как получить из него данные для обработки в 1С.
ЧтениеXML,  ДокументDOM, XPath, ФабрикаXDTO, ПакетыXDTO в сравнении.

 

При обмене данными, как бы он не производился  ( через файлы, через HTTP запросы или еще каким либо другим путем ) все равно основным форматом обмена является XML. В 1С существует несколько способов обработки XML  документов    какой из них выбрать по критерию логической простоты и быстродействия?  Для практической проверки различных методов был создан XML документ вида


 

Задачей всех проверяемых методов было получение из XML файла массива, состоящего из структур со свойствами Номер, Дата, Поставщик, Состав, причем свойство Состав само является массивом структур со свойствами Номенклатура и Количество.  

 

Первый метод – простое последовательное чтение XML.


 

Текущее положение в XML документе отслеживается в переменной  ТекущийПуть и при поступлении текстового узла на  основании этой переменной заполняются (или игнорируются) соответствующие данные 1С. Конечно, не совсем корректно оценивать логическую сложность программы в строках исходного текста, но тем не менее это самый большой объем  из всех методов  - 64 строки. Что касается быстродействия (оно проверялось на компьютере памятью в 8 Гб и процессором Intel i7 2.2 Ггц, было создано два файла один на 10 тысяч записей объемом 10 мегабайт, другой на 100 тысяч и 100 мегабайт соответственно) , то на файле 10 000 записей полная обработка заняла 30 секунд  и на файле в 100 мегабайт линейно увеличилась в 10 раз.

 

Второй метод – получение из XML файла документа DOM и последовательный перебор всех узлов полученного документа

 

 

Логически этот метод весьма незначительно проще прямого ЧтенияXML (57 строк кода против 62), а вот с быстродействие картина интересная: для файла в 10 тысяч записей быстродействие составило 12 секунд (быстрее более чем в два раза) ,  но для файла со 100 тысячами записей резко поднялась до 1000 секунд (медленнее более чем в три раза).

  

Третий метод подобен второму, но вместо последовательного перебора узлов применен отбор требуемых узлов в DOM документе  с помощью  выражений XPath .

 

Этот метод немного проще чем простой перебор узлов в DOM документе, но быстродействие … При 10 000 записей 69 секунд , а для 100 000 обработка длилась более часа, так и не завершилась, после чего была снята принудительно.

 

Очевидно метод с использованием DOM, в особенности при поиске узлов документа XPath выражениями, надо использовать только для небольших изменений DOM документа со сложной структурой узлов.

 

 

Все последующие методы используют для разбора XML документа фабрику XDTO.

 

Четвертый метод использует метод  ПрочитатьXML глобальной ФабрикиXDTO. При этом тип получаемого объекта не указывается, его определяет сам метод фабрики. Тут есть маленькая тонкость – в случае, если тип получаемого объектаXDTO не указан, фабрика не всегда может самостоятельно определить что она получила одиночный объект или список из нескольких одинаковых объектов ( в нашем примере если ПриходныйОрдер в XML документе будет один фабрика посчитает что ПриходыXDTO.ПриходныйОрдер это одиночный объект а не список).



 

Данный метод не только заметно проще, чем все предыдущие, но и показывает отличное быстродействие  на 10 000 записях 4,6 секунды, а при 100 000 обработка длится 46 секунд. Что показывает линейную зависимость от объема обрабатываемого файла.

 

Пятый метод схож с предыдущим, но глобальной фабрике XDTO подается на вход не только сам XML документ, но и его тип. Этот тип берется из ветки конфигурации XDTO-пакеты. (Если те кто передает вам XML данные хорошие люди, то они должны передать вам и XML схему данных – файл с расширением .xsd  из которого вы и создаете в своей конфигурации  XDTO-пакет – команда импорт XML схемы …, если нет требуемый пакет обычно не сложно создать вручную, анализируя переданные XML данные).

 

Один нюанс – частенько XML данные передают без указания URI пространства имен ( атрибут  xmlns). Если это так, то необходимо самому добавить недостающий атрибут в XML данные, что и делают первые три оператора процедуры.

 

Метод замечательный, как по простоте создания, так и по быстродействию – 3,1 секунды на 10 000 записях.

 

И последний метод. Это скорее некоторая модификация пятого метода, но для условия, что не используется объект конфигурации XDTO-пакет, и фабрика XDTO создается на ходу из текстового описания XML схемы. (Примечание 1-Конечно можно загнать схему в текстовый макет, и оттуда уже использовать, но для учебной программы так нагляднее. 2-Текстовый вариант XML схемы можно например  получить создав XDTO-пакет в любой конфигурации XDTO-пакет и  выполнив команду Экспорт XML схемы..)


В прилагаемой конфигурации все методы разбора XML представлены в общей команде РазобратьXML, там же в другой общей команде СоздатьXML представлены средства для создания тестовых данных.

Скачать файлы

Наименование Файл Версия Размер
РазборXML.cf

.cf 18,49Kb
05.11.14
706
.cf 18,49Kb 706 Скачать

Специальные предложения

Вознаграждение за ответ
Показать полностью
Комментарии
Избранное Подписка Сортировка: Рейтинг 1-го уровня
5. Armando 1393 05.11.14 21:17 Сейчас в теме
Позволю себе процитировать ИТС. В контексте этой статьи полезно будет.

ИТС: Оптимизация использования оперативной памяти:
Недопустимо работать с большими XML документами с помощью объектов встроенного языка, предназначенных для обработки файлов целиком: текстовые документы в ТекстовыйДокумент, XML в ДокументDOM и HTML в ДокументHTML, а также создавать в памяти XDTO-пакеты размером с весь XML-файл целиком.

В противном случае, весь файл загружается в оперативную память целиком. Исключения составляют отдельные случаи, когда необходим произвольный доступ к содержимому файла, к какой-то конкретной его части.

Следует использовать объекты для последовательной записи и последовательного чтения: ЧтениеXML, ЧтениеТекста, ЗаписьXML, ЗаписьТекста, с помощью которых можно прочитать файл порциями и расходовать память экономно.

При использовании механизмов XDTO неправильно зачитывать в память весь XML-файл целиком (ФабрикаXTDO.ПрочитатьXML(ЧтениеXML)). Вместо этого следует зачитывать XML-файл последовательно, с помощью объекта ЧтениеXML, а его отдельные фрагменты (теги) десериализовывать с помощью фабрики XDTO.
Razlagutt; Gendelf; user712426; 1v7; user828703; CyberCerber; rayastar; for_questions; zzz14; echo77; NN2P; veiuper; alevnev; infostart user; jobkostya1c8; Resha; alexscamp; bashirov.rs; mgn; FrLenok; help1Ckr; thevist; AfroditaS; Silenser; mr.Samuelson; logarifm; almierm; dj_serega; nsirotkin@mail.ru; kirillkr; awk; rtnm; +32 Ответить 2
3. salexdv 1612 05.11.14 18:14 Сейчас в теме
Приходится часто обрабатывать файл известной структуры размером от 300 Мб до 1.5 Гб. Для быстрого чтения использую простую схему.
1. Из файла выделяются блоки (у меня называются "offer"), которых может быть до нескольких сотен тысяч. Делается это через чтение текста с разделителем.
// Чтение файла по блокам <offer>...</offer>
Файл = Новый ЧтениеТекста(ИмяФайла, , "</offer>");	
ТекСтрока = Файл.ПрочитатьСтроку();
Пока ТекСтрока <> Неопределено Цикл
     // Обработка блока
    ТекСтрока = Файл.ПрочитатьСтроку();


2. Каждый блок разбирается с помощью регуляного выражения <ИмяСвойства>(.*?)</ИмяСвойства>

Последний файл размером 460 Мб, обработал за 2 минуты. Но тут надо учитывать, что обработка включала в себя еще чтение/запись в БД.
Памяти такой метод практически не потребляет, правда плохо подходит для файлов со сложной структурой (большим уровнем вложенности)
user712426; acanta; tovpeko; cry; mirco; zxcvb98765; tsukanov; for_questions; NN2P; zerg17; yuraer; veiuper; Зеленоград; Valerich; +14 Ответить 2
52. sebum 19.02.15 13:37 Сейчас в теме
Статья хорошая, а вот файл скачивать не стоит.
В прилагаемой конфигурации есть только два первых способа разбора, остальные отсутствуют. Копипастинг не получится :)
Листинги же этих способов в тексте даны мелкими картинками, на небольшом экране сломаешь глаза.
Но в остальном - прекрасное пособие для начинающих.
user712426; FSerg; alevnev; JohnyDeath; stupidgamer; +5 Ответить 1
59. jobkostya1c8 11.10.15 21:56 Сейчас в теме
Напоследок, еще проблемы новой технологии "в массы":
1. Когда всего один подчиненный узел тип ОбъектXDTO, а когда больше одного СписокXDTO - но это уже решается "человеческими" методами при переборе коллекции (выяснилось при разборе большого файла.
2. Даже при ручном указании в схеме xsd типов даты "datatime" и вещественного числа "decimal" (в обработке ) вместо string все равно приходится "парсить" вручную как при классическом методе ЧтениеXML конструкцией XMLЗначение(Тип("Дата") , УзелДог.ДатаДоговора ); или XMLЗначение(Тип("Число") , УзелДог.ВещественноеЧисло10_2); Возможно я тут ошибся в типах схемы (есть ли более совершенные редакторы с автораспознаванием типов)? Не зря автор статьи писал про то что он делает схему руками (может не те параметры командной строки)? Фрагмент схемы ниже:
<xs:element name="DOG" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="НомерДоговора" type="xs:string" />
<xs:attribute name="ДатаДоговора" type="xs:datatime" />
<xs:attribute name="Наименование" type="xs:string" />
<xs:attribute name="Организация" type="xs:string" />
<xs:attribute name="ВидДоговора" type="xs:string" />
<xs:attribute name="ВидВзаиморасчетов" type="xs:string" />
<xs:attribute name="УстановленСрокОплаты" type="xs:string" />
<xs:attribute name="СрокОплаты" type="xs:string" />
<xs:attribute name="НаименованиеДляСчетаФактурыНаАванс" type="xs:string" />
<xs:attribute name="ПорядокРегистрацииСчетовФактурНаАвансПоДоговору" type="xs:string" />
<xs:attribute name="Комментарий" type="xs:string" />
<xs:attribute name="ВещественноеЧисло10_2" type="xs:Decimal" />
<xs:attribute name="N" type="xs:string" />
</xs:complexType>
</xs:element>
Показать


Ниже окончательный вариант который читает большие файлы:
// ПРОБЛЕМА № 1 - есть ли узел (и есть ли метод без Попытка-исключение?)
			Попытка	
				УзлыДог = Узел.DOG;
			Исключение
				Продолжить; // Нет узла! Пропускаем!
			КонецПопытки;
			
			// ПРОБЛЕМА № 2 - сколько этих узлов 1 или больше (тип то разный ОбъектXDTO или СписокXDTO из этих самых объектов)? 
			Если ТипЗнч(УзлыДог) = Тип("СписокXDTO") Тогда
				Для Каждого УзелДог из УзлыДог Цикл
					Сообщить(""+Символы.Таб+"Договор № "+УзелДог.N+", Наименование "+УзелДог.Наименование+" - (Тип узла СписокXDTO у КА много договогов)"); // Обработка всех договоров (если есть) контрагента как подчиненных узлов
					//F = 1/0; // Модель ошибки!
				КонецЦикла;
			ИначеЕсли ТипЗнч(УзлыДог) = Тип("ОбъектXDTO") Тогда
				УзелДог = УзлыДог; // Еще заплатка для однотипности передаваемого объекта "в обработку"
				XMLЗначение(Тип("Дата") , УзелДог.ДатаДоговора ); // - Опять старые проблемы - вручную десериализовывать по типам XMLЗначение(Тип("Дата") , УзелДог.ДатаДоговора ) 
				Сообщить(""+Символы.Таб+"Договор № "+УзелДог.N+", Наименование "+УзелДог.Наименование+" - (ОДИН договор у КА и тип узла ОбъектXDTO"); // Обработка всех договоров (если есть) контрагента как подчиненных узлов
			Иначе
				ВызватьИсключение("Неизвестный тип XDTO в строке ");
			КонецЕсли;
Показать

Но, в целом не нужно вручную мучиться с набивкой структуры для дальнейшей обработки. Для простых файлов с одинаковой структурой такой метод более "прост" по сравнению с "классическим" если XML получен не из 1С.
Ниже результат чтения в строке сообщений 1С:
Начали чтение ХМЛ
Контрагент № 1, Наименование учредители
Контрагент № 2, Наименование Фирма1
Договор № 3, Наименование Основной договор - (Тип узла СписокXDTO у КА много договогов)
Договор № 4, Наименование основной - (Тип узла СписокXDTO у КА много договогов)
Контрагент № 5, Наименование Тесть КА и 1 договор (тип объектXDTO, а не список)
Договор № 6, Наименование Когда всего 1 договор (тип объектXDTO, а не список) - (ОДИН договор у КА и тип узла ОбъектXDTO
Контрагент № 7, Наименование Последний без подчиненных договоров!
Прочитали ХМЛ

Вот такое вот "веселое" знакомство при переходе на новую технологию (еще если глюков не будет).
Прикрепленные файлы:
тестТехнологииЧтенияXMLЧерезXDTO.epf
in.xml
Vida; eeeio; st4rk; +3 Ответить
53. binex 265 21.07.15 15:56 Сейчас в теме
Как можно проверить наличие свойства некоторго тега у XML?

Использую 4-вариант (ФабрикаХДТО), некоторые поля могут быть нобязательные, поэтому приходится проверять их наличие самописной функцией (вернее дёргать значение свойства через неё):


Функция СвойствоХДТО(пОбъектХДТО,пИмяСвойства,пЗначениеПоУмолчанию = Неопределено)
	лКоллекцияСвойств = пОбъектХДТО.Свойства();
	лСвойствоХДТО = лКоллекцияСвойств.Получить(пИмяСвойства);
	Если лСвойствоХДТО = Неопределено Тогда
		Возврат пЗначениеПоУмолчанию;
	Иначе
		Возврат пОбъектХДТО.Получить(лСвойствоХДТО);
	КонецЕсли;
КонецФункции

Показать
user712426; HIVvich; jobkostya1c8; +3 Ответить
16. Trotter_NN 07.11.14 08:11 Сейчас в теме
Скачал конфу, в ней нету всех методов, а качал для того что бы посмотреть функцию МассивВСтроку() Не могли бы поделится ?)) А то ЗначениеВСтрокуВнутр даёт немного другой результат
Lutcenko; baracuda; +2 Ответить 1
25. anton.fly7 149 12.11.14 09:27 Сейчас в теме
>>...то они должны передать вам и XML схему данных – файл с расширением .xsd ...

их файла xml можно самому собрать xsd-схему, есть консольная утилита xsd.exe
Прикрепленные файлы:
xsd.exe
AnL24; jobkostya1c8; +2 Ответить 2
73. 🅵🅾️🆇 20.02.18 16:01 Сейчас в теме
Возможно сочтете за некропостинг, но теперь многое из написаного можно записать еще более емко с помощью: ЗаполнитьЗначениеСвойств()

Вот упрощенный вариант понятного чтения в несколько строк небольших файлов, на примере типичного документа с табличной частью (сверка актов из 1с 77):
ЧтениеXML	= Новый ЧтениеXML;
ЧтениеXML.ОткрытьФайл(Файл.ПолноеИмя);
АктыXDTO	= ФабрикаXDTO.ПрочитатьXML(ЧтениеXML);
Акты		= Новый Структура("ОрганизацияИНН,ОрганизацияНаименование,КонтрагентИНН,КонтрагентНаименование,Договор,ОрганизацияФИО,ОрганизацияДолжность,КонтрагентФИО,КонтрагентДолжность,ДатаНачала,ДатаОкончания,Операции");
ЗаполнитьЗначенияСвойств(Акты, АктыXDTO);
Акты.Вставить("Операции", Новый Массив);
Для Каждого ОперацияXDTO Из АктыXDTO.Операция Цикл
	Операция	= Новый Структура("Номер,Операция,Дебет,Кредит,Согласован");
	ЗаполнитьЗначенияСвойств(Операция, ОперацияXDTO);
	Акты.Операции.Добавить(Операция);	
КонецЦикла; // Для Каждого Операция Из АктыXDTO.Операция
// Не забываем закрывать, господа:
ЧтениеXML.Закрыть();
Показать
mst; Vida; +2 Ответить
20. logarifm 1076 07.11.14 13:14 Сейчас в теме
Статья шикарная. Радует глаз сравнение визуально в коде всех методов. Я много занимаюсь обменами и всегда использую методы последовательного чтения ХМЛ. Попробую применить два последних метода. Автору однозначно плюсую.
1. caponid 05.11.14 16:20 Сейчас в теме
Поправь картинку для XPath - слетела
54. Valerich 1677 18.08.15 08:32 Сейчас в теме
Еще бы для сравнения варианты обработки с преобразованием XSLT. Позволяет несколькими строками кода получить на выходе требуемый объект в терминах 1С. Вот только для больших файлов XML (1 ГБ и больше) сложность возникает - памяти не всегда хватает.

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

А то в последнее время приходится работать с XML сложной структуры просто огромных размеров. приходится извращаться. Иначе клиент валится по нехватке памяти. Про быстродействие молчу.
jobkostya1c8; +1 Ответить 1
48. Yashazz 3262 11.01.15 20:01 Сейчас в теме
Обнаружил пренеприятнейшую вещь - при действительно больших объёмах (4 585 894 строк, 222 411 694 символов) вызываемый из 1С объект Shell.RegExp валит 1С по недостатку памяти, даже на достойных серверах. Сами куски мелкие, рубить их ЧтениемТекста по разделителю - мало смысла. Жаль, что нет метода Прочитать(КоличествоСтрок,Разделитель), а есть только Прочитать(КолвоСимволов).

Кстати, никто не в курсе, ЧтениеУзловDOM сразу берёт всё (т.е. требует памяти) или идёт последовательно?
jobkostya1c8; +1 Ответить
64. timeforlive 15 02.03.17 06:04 Сейчас в теме
Автор, необходимо предупреждать в начале поста, что для .cf нужна платформа 8.3.5

Почему не прикрепить .rar, в котором прикрепить как .cf, так и сами обработки отдельно?
77. boss_kuz 89 04.08.18 06:15 Сейчас в теме
Почему в фале cf нет примеров кода использованного на картинках ? Лажа...
10. ZMGMSC 68 06.11.14 12:29 Сейчас в теме
Положительных голосов на порядок больше чем скачиваний... Странно.
46. alexscamp 28.12.14 19:55 Сейчас в теме
Статья отличная, просто учебник для всех, побольше бы таких, ну и дополнение про оптимизацию, память и ссылку на ИТС было бы неплохо указать - тогда было бы просто идеально! Буду рекомендовать коллегам к прочтению :)
85. vlabal 27.02.19 12:13 Сейчас в теме
Большое спасибо автору за примеры!
Возникла неудобная ситуация с обработкой списка в XML.
Если в списке одна строка, то возникает ошибка при попытке выполнить цикл, т.к. ЗаявкаXDTO.POSBreakages.POSBreakage является почему-то объектом XDTO, а если несколько строк, то ЗаявкаXDTO.POSBreakages.POSBreakage видится как СписокXDTO и обход проходит нормально, можно выкрутиться через попытку исключение, но красивое решение пока не удалось найти.

Для Каждого СтрокаPOSBreakages Из ЗаявкаXDTO.POSBreakages.POSBreakage Цикл

СтрокаPOSBreakagesСтруктура = Новый Структура("POSBreakage_ID,SC_POSBreakage_ID,POSBreakage_Name");
СтрокаPOSBreakagesСтруктура.POSBreakage_ID = СтрокаPOSBreakages.POSBreakage_ID;
СтрокаPOSBreakagesСтруктура.SC_POSBreakage_ID = СтрокаPOSBreakages.SC_POSBreakage_ID;
СтрокаPOSBreakagesСтруктура.POSBreakage_Name = СтрокаPOSBreakages.POSBreakage_Name;
ДокументЗаявкаСтруктура.POSBreakages.Добавить(СтрокаPOSBreakagesСтруктура);

КонецЦикла;

Предложите лучший вариант.
89. t_yuri 17 01.08.19 13:42 Сейчас в теме
Почему в cf не все указанные варианты примеров?

В разборе вижу только:
// Простой обход дерева
// Использование XPath

Где другие варианты??
24. PiccaHut001 10.11.14 11:34 Сейчас в теме
когда-нибуть пригодится
29. bulpi 174 12.11.14 14:03 Сейчас в теме
Сразу, сходу, не дочитав статью :
В первом методе, ИМХО, быстродействие тормозит вот это : МассивВСтроку(). Проверь без него, т.е. вообще не нужно использовать ТекущийПуть. Он нужен только в том случае, если есть неуникальные имена узлов, а в примере их нет
22. Иной 08.11.14 23:53 Сейчас в теме
Спасибо. У самого времени не было потестить все методы.
30. bulpi 174 12.11.14 14:08 Сейчас в теме
В примере для метода 4 один абзац написан дважды.
+