Эффективное объединение нескольких больших xml-файлов в один

StackOverflow https://stackoverflow.com/questions/2275860

  •  21-09-2019
  •  | 
  •  

Вопрос

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

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

Позвольте мне объяснить более подробно.У меня есть несколько множественных xml-файлов (предпочтительнее InputSteam), которые следует проанализировать.Файлы или входной поток выглядят следующим образом

входной поток 1

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

входной поток 2

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

входной поток 1+входной поток 2+...+ Входной поток N = результирующий 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

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

Это было полезно?

Решение 3

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

  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 пустыми.Все остальные методы (characters, startElement, endElement) будут перенаправлены в finalHandler.Метод customHandler.createNextInputStream возвращает null, если прочитаны все входные потоки.

Другие советы

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

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

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

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

наиболее эффективным способом объединения файлов является использование функции вырезания и вставки на уровне байтов, предлагаемой VTD-XML-ФАЙЛ, АФАИК.Вы берете оба файла, разбираете их на объекты VTDNav, затем создаете экземпляр объекта XMLModifier, извлекаете фрагменты из второго файла и вставляете их в первый файл...это должно быть намного эффективнее, чем саксофон..Кроме того, результирующий 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");   
    }
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top