Пообъектный разбор разметки

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

UPD

Конфигурацию можно потрогать на github

Суть

Когда смотришь на очередной xml файл, который тебе прислали для загрузки заказов/прайс-листа/номенклатуры/еще чего-нибудь, в глаза бросается очевидная объектная сущность xml. Почему же нельзя каким-нибудь хитрым образом загрузить это дело так, чтобы просто обращаться к свойствам через точки, не заморачиваясь с предварительным описанием ожидаемой структуры и не выкуривая мануалы по XDTO (что, кстати, советую сделать вне зависимости от)? Можно, конечно, загрузить все в структуры и массивы, но имена тэгов не всегда возможны для ключей структуры, да и загружать файл целиком в оперативную память может быть черевато уже при достижении им 10 Мбайт. 1С не разрешает нам создавать свои объекты, но кое-чего можно добиться и с помощью стандартных. Данная обработка как раз об этом.

Cразу к делу

Пример xml:

<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="myfile.xsl" ?>
<bookstore specialty="novel">
<book style="autobiography">
<author>
<first-name>Joe</first-name>
<last-name>Bob</last-name>
<award>Trenton Literary Review Honorable Mention</award>
</author>
<price>12</price>
</book>
<book style="textbook">
<author>
<first-name>Mary</first-name>
<last-name>Bob</last-name>
<publication>Selected Short Stories of
<first-name>Mary</first-name>
<last-name>Bob</last-name>
</publication>
</author>
<editor>
<first-name>Britney</first-name>
<last-name>Bob</last-name>
</editor>
<price>55</price>
</book>
<magazine style="glossy" frequency="monthly">
<price>2.50</price>
<subscription price="24" per="year"/>
</magazine>
<book style="novel" id="myfave">
<author>
<first-name>Toni</first-name>
<last-name>Bob</last-name>
<degree from="Trenton U">B.A.</degree>
<degree from="Harvard">Ph.D.</degree>
<award>Pulitzer</award>
<publication>Still in Trenton</publication>
<publication>Trenton Forever</publication>
</author>
<price intl="Canada" exchange="0.7">6.50</price>
<excerpt>
<p>It was a dark and stormy night.</p>
<p>But then all nights in Trenton seem dark and
stormy to someone who has gone through what
<emph>I</emph> have.</p>
<definition-list>
<term>Trenton</term>
<definition>misery</definition>
</definition-list>
</excerpt>
</book>
<my:book xmlns:my="uri:mynamespace" style="novel" price="29.50">
<my:title>Who's Who in Trenton</my:title>
<my:author>Robert Bob
<my:first-name>Robert</my:first-name>
<my:last-name>Bob</my:last-name>
</my:author>
</my:book>
</bookstore>

Пример разбора:

Разбор = Обработки.РазборРазметки.ИнициализироватьПоиск("book", ПутьКФайлу, Истина, "book.style", "novel");

Пока Разбор.Следующий() Цикл

ИмяАвтора  = Разбор.ЗначениеТэга("book.author.first-name", Истина);
ФамилияАвтора = Разбор.ЗначениеТэга("book.author.last-name", Истина);

Если Разбор.ЕстьТэг("book.author.degree") Тогда

СтепениАвтора = Разбор.Тэги("book.author.degree");

Сообщить(ИмяАвтора + " " + ФамилияАвтора + " - обладатель следующих степеней:");

Для Каждого СтепеньАвтора Из СтепениАвтора Цикл

Университет = СтепеньАвтора.ЗначениеАтрибута("degree.from");
Степень = СтепеньАвтора.ЗначениеТэга("degree");

Сообщить("    Университет: " + Университет + "; Степень: " + Степень);

КонецЦикла;
Иначе
Сообщить(ИмяАвтора + " " + ФамилияАвтора + " не имеет степеней.");
КонецЕсли;
КонецЦикла;

Результат:

Toni Bob - обладатель следующих степеней:
Университет: Trenton U; Степень: B.A.
Университет: Harvard; Степень: Ph.D.
Robert Bob не имеет степеней.

Как это и что под капотом

Чаще всего разбираемый файл состоит из кучи однотипных объектов. Загружать в оперативную память весь файл целиком — плохая идея, но вот каждый из этих объектов по отдельности без проблем в ней поместится в 1368 случаях из 1369. Обработка сканирует файл (читает по узлам с помощью штатного ЧтениеXML) и, когда находит нужный нам объект загружает его целиком и предоставляет интерфейс доступа к значениям и атрибутам вложенных тэгов. В общем, ведет себя практически как выборка или то же ЧтениеXML.

API 

Обработки.РазборРазметки.ИнициализироватьПоиск("book", ПутьКФайлу, Истина, "book.style", "novel");

Создает новую обработку и задает, что, где и как будем искать. В данном случае будем искать тэги «book» в файле по адресу ПутьКФайлу. Искать будем по локальным именам (в примере последний «book» имеет префикс и его полное имя будет «my:book»). При этом мы хотим читать только те узлы, у которых свойство «book.style» равно «novel». При чем, в данном случае не важно, значение это вложенного тэга или атрибута.

Разбор.Следующий()

Начинает (или продолжает) читать файл в поисках того, что мы попросили. Как находит, читает нужный тэг целиком и говорит, что готов. Если доходит до конца файла, говорит, что все.

ЗначениеТэга("book.author.first-name", Истина);
ЗначениеАтрибута("degree.from");

Получает значение тэга (текст между открывающим и закрывающим тэгом)  или значение атрибута соответственно. Второй реквизит отвечает за то, будет ли поиск по локальным именам, или по полным. В случае атрибута адрес задается как адрес тэга и в конце через точку имя атрибута. Вообще была мысль не разделять тэги и атрибуты и сделать просто «ЗначениеСвойства». Пока не попался xml с атрибутом и вложенным тэгом с одинаковыми именами и разными значениями.

Тэги("book.author.degree");

Для загрузки табличных частей и других дублирующихся тэгов. Ищет заданные тэги внутри текущего, возвращает массив таких же объектов РазборРазметки, как и исходный, только каждый с содержанием, соответствующего тэга из множества найденных. В них тот же API доступа к содержанию но без возможности делать Следующий().

ЕстьТэг("book.author.degree")
а также
ЕстьАтрибут(ПутьКАтрибуту, ПоЛокальнымИменам=Ложь)
ЕстьСвойство(ПутьКСвойству, ПоЛокальнымИменам=Ложь)

Просто возвращают, есть ли вложенный тэг, атрибут или (тэг или атрибут). 

Еще на данном этапе есть экспериментальный

Текст(ПутьКТэгу, ПоЛокальнымИменам=Ложь)

По задумке должен возвращать текст, очищеный от тэгов. Например, <p>Paragraph <h1>III</h></p> должен превратиться в «Paragraph III». В простых случаях работает, но тупит в случаях, подобных <p>Paragraph <h1>III</h> и какой-то еще текст в конце</p>. Но для такого парсинга есть другие инструменты, да и в практике 1С редко встречаются такие задачи.

Что еще

В модуле менеджера пример использования и простой набор тестов. На форме обработки интерфейс для запуска примера и тестов. Особой оптимизации и рефакторинга пока не было, но те задачи, для выполнения которых этот велосипед сооружался, выполняет вполне достойно. Хоть в некоторых случаях и медленнее, чем было бы какое-то менее универсальное решение. Но в случаях, когда скорость разработки важнее скорости исполнения (отчетный период там, или вторник, например) самое оно. В планах добавить кое-какие свистелки и оптимизмровать. Если уважаемое сообщество заинтересуется, можно и JSON запилить.

8 Comments

  1. kiruha

    Попробуйте следущий простой код

    ЧтениеXML = Новый ЧтениеXML;
    ЧтениеXML.ОткрытьФайл(ИмяФайлаXML);
    СхемаДокумента = ФабрикаXDTO.ПрочитатьXML(ЧтениеXML);
    

    и посмотрите что в СхемаДокумента.

    Не нравится XDTO по идейным соображениям — легко преобразуется в структуру или соответствие

    Reply
  2. 9-pm

    (1) Пробовал, конечно. ПрочитатьXML читает весь файл целиком в оперативную память. Вот тут цитировали ИТС, которое не советует так делать с большими файлами. А против XDTO ничего не имею. Даже напротив — в случаях когда имеешь дело со стабильными XML одного и того же формата (обмен со своим сайтом, например), то гораздо правильнее вообще описать пакет в конфигурации и не переживать. А у меня специфика такая, что каждую неделю новая загрузка из какого-нибудь непредсказуемого источника и непредсказуемого формата, которая может больше никогда и не повториться.

    Reply
  3. spezc

    Обработку не пробовал, но одобряю. Держи звезду

    Reply
  4. davdykin

    Складывается ощущение что если освоить этот инструмент в своей нише он может стать весьма не плохим подспорьем.

    Reply
  5. 9-pm

    (4)

    (3) Благодарю. Если хочется потрогать, а стартмани жалко, то вот ссылка на github )

    Reply
  6. davdykin

    Вопрос больше во времени и практических задачах, но за ссылку в любом случаи спасибо!

    Reply
  7. spezc

    (5) Спасибо, маней достаточно) но за ссылку спасибо. За это таки скачаю обработку)

    Reply
  8. Светлый ум

    +1, взял на вооружение

    (за бесплатность отдельное спасибо)

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *