Эффективное объединение нескольких больших XML-файлов в один - PullRequest
4 голосов
/ 16 февраля 2010

Я искал в Интернете и искал stackoverflow вверх и вниз. Нет решения. Хотя я нашел решения, как сделать это в чистом xslt здесь .

Но проблема в том, что полученный xml будет иметь размер в несколько сотен МБ. Так что я должен сделать это с SAX в Java. (пожалуйста, не используйте решение xslt, хотя я пометил его xslt; -))

Позвольте мне объяснить более подробно. У меня есть несколько файлов XML (предпочтительно InputSteam's), которые должны быть проанализированы. Файлы или InputStream выглядят как

inputstream1

<root>
  <doc>
    <tag>test1</tag>
  </doc>
  <doc>
    <tag>test2</tag>
  </doc>
  ...
</root>

inputstream2

<root>
  <doc>
    <tag>test3</tag>
  </doc>
  <doc>
    <tag>test4</tag>
  </doc>
  ...
</root>

inputtream1 + inputtream2 + ... + inputtreamN = результирующий xml . Это будет выглядеть как

<root>
  <doc>
    <tag>test1</tag>
  </doc>
  <doc>
    <tag>test2</tag>
  </doc>
  ...
   <doc>
    <tag>test3</tag>
  </doc>
  <doc>
    <tag>test4</tag>
  </doc>
  ...
</root>

У кого-нибудь есть решение или ссылка для этого? Это возможно через реализацию пользовательского InputSource или я должен использовать пользовательский ContentHandler? Или это возможно с joost / stx ?

Хорошо, если бы я мог использовать ContentHandler, то мог бы применить некоторые незначительные преобразования (я уже реализовал это). Но тогда проблема в том, что я не знаю, как передать несколько файлов или InputStream как InputSource:

XMLReader xmlReader = XMLReaderFactory.createXMLReader();
xmlReader.setContentHandler(customHandler);
xmlReader.parse(getInputSource()); // only one InputStream possible

или я должен анализировать InputStreams непосредственно в моем ContentHandler?

Ответы [ 4 ]

2 голосов
/ 16 февраля 2010

Я сам этого не делал, но вспомнил, что видел статью IBM для разработчиков, которая выглядела так, как будто это довольно легко.

Сейчас немного устарело, но попробуйте http://www.ibm.com/developerworks/xml/library/x-tipstx5/index.html

Это StAX вместо SAX. Я не уверен, что текущие JDK включают StAX. Если нет, то, вероятно, вы можете получить его от http://stax.codehaus.org/

1 голос
/ 16 февраля 2010

Возможно, вы захотите взглянуть на платную версию Saxon. Он может обрабатывать XSLT на лету, не требуя полной памяти в памяти.

0 голосов
/ 18 февраля 2010

Самый эффективный способ объединения файлов - использовать функцию вырезания и вставки на уровне байтов, предлагаемую VTD-XML , AFAIK. Вы берете оба файла, анализируете их в объектах VTDNav, затем создаете экземпляр объекта XMLModifier, извлекаете фрагменты из второго файла и вставляете их в первый файл ... который должен быть намного более эффективным, чем SAX. Также получающийся в результате XML получает записанное направление в файл - нет необходимости хранить его в памяти. Ниже приведен полный код менее чем за 20 строк ...

import com.ximpleware.*;
import java.io.*;

public class merge {
    // merge second.xml into first.xml assuming the same encoding
    public static void main(String[] s) throws VTDException, IOException{
        VTDGen vg = new VTDGen();
        if (!vg.parseFile("d:\\xml\\first.xml", false))
            return;
        VTDNav vn1=vg.getNav();
        if(!vg.parseFile("d:\\xml\\second.xml", false))
            return;
        VTDNav vn2 = vg.getNav();
        XMLModifier xm = new XMLModifier(vn1);
        long l = vn2.getContentFragment();
        xm.insertBeforeTail(vn2, l);
        xm.output("d:\\xml\\merged.xml");   
    }
}
0 голосов
/ 17 февраля 2010

Мне наконец удалось это сделать с помощью следующего фрагмента:

  finalHandler = new StreamResult(new OutputStreamWriter(System.out));
  // customHandler extends DefaultHandler
  CustomTransformerHandler customHandler = new CustomTransformerHandler(
         finalHandler);
  customHandler.startDocumentExplicitly();
  InputStream is = null;
  while ((is = customHandler.createNextInputStream()) != null) {
    // multiple inputStream parsing
    XMLReader myReader = XMLReaderFactory.createXMLReader();
    myReader.setContentHandler(customHandler);
    myReader.parse(new InputSource(is));
  }
  customHandler.endDocumentExplicitly();

Важной частью было оставить методы startDocument и endDocument пустыми. Все остальные методы (символы, startElement, endElement) будут перенаправлены в finalHandler. Метод customHandler.createNextInputStream возвращает ноль, если считываются все входные потоки.

...