Как превратить огромные XML-файлы в Java? - PullRequest
7 голосов
/ 05 мая 2010

Как следует из названия, у меня есть огромный XML-файл (ГБ)

<root>  
<keep>  
   <stuff>  ...  </stuff>  
   <morestuff> ... </morestuff>  
</keep>  
<discard>  
   <stuff>  ...  </stuff>  
   <morestuff> ... </morestuff>
</discard>  
</root>  

и я бы хотел превратить его в гораздо меньший, в котором осталось бы только несколько элементов.
Мой парсер должен сделать следующее:
1. Проанализируйте файл, пока не запустится соответствующий элемент.
2. Скопируйте весь соответствующий элемент (с дочерними элементами) в выходной файл. перейти к 1.

шаг 1 прост с SAX и невозможен для DOM-парсеров.
шаг 2 раздражает с SAX, но легко с DOM-Parser или XSLT.

и что? - Есть ли удобный способ объединить SAX и DOM-Parser для выполнения задачи?

Ответы [ 7 ]

10 голосов
/ 05 мая 2010

StAX может показаться одним очевидным решением: это синтаксический анализатор, а не «push» SAX или «буферизация всего» в DOM. Не могу сказать, что я использовал это все же. Поиск «Учебник по StAX» может пригодиться:)

9 голосов
/ 05 мая 2010

Да, просто напишите обработчик содержимого SAX, и когда он встретит определенный элемент, вы создадите дерево dom для этого элемента. Я сделал это с очень большими файлами, и это работает очень хорошо.

На самом деле это очень просто: как только вы встречаете начало нужного вам элемента, вы устанавливаете флаг в вашем обработчике контента, и с этого момента вы отправляете все в DOM Builder. Когда вы сталкиваетесь с концом элемента, вы устанавливаете флаг в false и выписываете результат.

(Для более сложных случаев с вложенными элементами с одинаковыми именами элементов вам потребуется создать стек или счетчик, но это все еще довольно просто сделать.)

5 голосов
/ 06 мая 2010

Я получил хороший опыт работы с STX ( Потоковые преобразования для 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», что означает, что соответствующий шаблон из group 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>

Необычное форматирование является результатом пропуска текстовых узлов, содержащих символы новой строки вне элементов child.

3 голосов
/ 05 мая 2010

Поскольку вы говорите о ГБ, я бы предпочел расставить приоритеты в использовании памяти при рассмотрении. SAX требует около 2-х раз памяти, как большой документ, в то время как DOM должен быть как минимум 5 раз Таким образом, если размер вашего XML-файла составляет 1 ГБ, для DOM потребуется минимум 5 ГБ свободной памяти. Это уже не смешно. Так что SAX (или любой другой вариант, например StAX) - лучший вариант здесь.

Если вы хотите наиболее эффективно использовать память, посмотрите на VTD-XML . Требуется только немного больше памяти, чем файл большого размера.

2 голосов
/ 05 мая 2010

Для такого большого 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
2 голосов
/ 05 мая 2010

Посмотрите на StAX , это может быть то, что вам нужно. Хорошее введение в IBM Developer Works .

0 голосов
/ 05 мая 2010

Вы можете сделать это довольно легко с XMLEventReader и несколькими XMLEventWriter с из пакета javax.xml.stream.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...