Эффективное объединение нескольких больших xml-файлов в один
Вопрос
Я поискал в Интернете и обыскал 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");
}
}