Поскольку в заголовке говорится это, у меня есть огромный XML-файл (ГБ)
<root>
<keep>
<stuff> ... </stuff>
<morestuff> ... </morestuff>
</keep>
<discard>
<stuff> ... </stuff>
<morestuff> ... </morestuff>
</discard>
</root>
и я хотел бы преобразовать его в намного меньший, который сохраняет только несколько элементов.
Мой синтаксический анализатор должен сделать следующее:
1. Синтаксический анализ через файл до соответствующего элемента запускается.
2. Скопируйте целый соответствующий элемент (с детьми) к выходному файлу. перейдите в 1.
шаг 1 легок с SAX и невозможен для DOM-синтаксических-анализаторов.
шаг 2 является раздражающим с SAX, но легким с DOM-синтаксическим-анализатором или XSLT.
и что? - там аккуратный способ объединить SAX и DOM-синтаксический-анализатор, чтобы сделать задачу?
Да, просто напишите обработчик содержимого SAX, и когда он встретит определенный элемент, вы построите на нем дерево доменов. Я делал это с очень большими файлами, и это работает очень хорошо.
На самом деле это очень просто: как только вы встречаетесь с началом нужного элемента, вы устанавливаете флаг в своем обработчике контента, и с этого момента вы перенаправляете все в построитель DOM. Когда вы встречаетесь с концом элемента, вы устанавливаете флаг в false и записываете результат.
(Для более сложных случаев с вложенными элементами с одним и тем же именем элемента вам потребуется создать стек или счетчик, но это все еще довольно легко сделать.)
StAX может показаться очевидным решением: это извлекающий синтаксический анализатор, а не «выталкивание» SAX или «буферизация всего вещь »подход DOM. Не могу сказать, что использовал его. Может пригодиться поиск «Учебник StAX» :)
Поскольку вы говорите о ГБ, я бы предпочел уделить приоритетное внимание использованию памяти при рассмотрении. SAX требуется примерно в 2 раза больше памяти, чем размер документа, в то время как DOM требуется, чтобы он был как минимум 5 раз. Итак, если ваш XML-файл имеет размер 1 ГБ, то для DOM потребуется минимум 5 ГБ свободной памяти. Это уже не смешно. Так что SAX (или любой его вариант, например StAX) - лучший вариант.
Если вы хотите максимально эффективно использовать память, посмотрите VTD-XML . Для этого требуется лишь немного памяти больше, чем размер файла.
Взгляните на StAX , это может быть то, что вам нужно. Есть хорошее введение в IBM Developer Works .
Я получил хороший опыт работы с STX (Streaming Transformations for XML). По сути, это потоковая версия XSLT, хорошо подходящая для разбора огромных объемов данных с минимальными затратами памяти. Она имеет реализацию на Java под названием Joost.
Должно быть легко придумать преобразование STX, которое игнорирует все элементы, пока элемент не совпадет с заданным XPath, копирует этот элемент и все его дочерние элементы (используя шаблон идентичности в группе шаблонов) и продолжает игнорировать элементы до следующего совпадения.
UPDATE
Я собрал преобразование STX, которое делает то, что, как я понимаю, вам нужно. Оно в основном зависит от возможностей только STX, таких как группы шаблонов и настраиваемые шаблоны по умолчанию.
<stx:transform xmlns:stx="http://stx.sourceforge.net/2002/ns"
version="1.0" pass-through="none" output-method="xml">
<stx:template match="element/child">
<stx:process-self group="copy" />
</stx:template>
<stx:group name="copy" pass-through="all">
</stx:group>
</stx:transform>
Параметр pass-through="none"
в stx:transform
настраивает шаблоны по умолчанию (для узлов, атрибутов и т.д.) так, чтобы они не производили никакого вывода, но обрабатывали дочерние элементы. Затем stx:template
соответствует XPath element/child
(это место, где вы помещаете ваше выражение соответствия), он "обрабатывает себя" в группе "copy", что означает, что соответствующий шаблон из группы name="copy"
вызывается на текущем элементе. Эта группа имеет pass-though="all"
, поэтому шаблоны по умолчанию копируют свои входные данные и обрабатывают дочерние элементы. Когда элемент element/child
завершается, управление передается обратно шаблону, который вызвал process-self
, и следующие элементы снова игнорируются. Пока шаблон снова не совпадет.
Ниже приведен пример входного файла:
<root>
<child attribute="no-parent, so no copy">
</child>
<element id="id1">
<child attribute="value1">
text1<b>bold</b>
</child>
</element>
<element id="id2">
<child attribute="value2">
text2
<x:childX xmlns:x="http://x.example.com/x">
<!-- comment -->
yet more<b i="i" x:i="x-i" ></b>
</x:childX>
</child>
</element>
</root>
Вот соответствующий выходной файл:
<?xml version="1.0" encoding="UTF-8"?>
<child attribute="value1">
text1<b>bold</b>
</child><child attribute="value2">
text2
<x:childX xmlns:x="http://x.example.com/x">
<!-- comment -->
yet more<b i="i" x:i="x-i" />
</x:childX>
</child>
Необычное форматирование является результатом пропуска текстовых узлов, содержащих новые строки вне дочерних
элементов.
Для такого большого XML-документа идеально подошло бы что-то с потоковой архитектурой, например Omnimark.
Это также не должно быть чем-то сложным. Скрипт Omnimark, подобный приведенному ниже, может дать то, что вам нужно:
process
submit #main-input
macro upto (arg string) is
((lookahead not string) any)*
macro-end
find (("<keep") upto ("</keep>") "</keep>")=>keep
output keep
find any
Вы можете сделать это довольно легко с помощью XMLEventReader
и нескольких XMLEventWriter
ов из пакета javax.xml.stream.