2010-02-16 2 views
4

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

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

Позвольте мне объяснить более подробно. У меня есть несколько нескольких xml-файлов (предпочтительнее InputSteam), которые необходимо проанализировать. Файлы или внешний вид 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> 

inputstream1 + inputstream2 + ... + inputstreamN = в результате XML. Это будет выглядеть как

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

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

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

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

ответ

0

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

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 возвращает значение null, если все входные потоки прочитаны.

1

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

+0

hmmh, в xslt вы можете искать первый узел и последний узел независимо от того, где вы находитесь. Т.е. все должно быть в памяти ... по определению xslt. Или что вы думаете? – Karussell

+0

Существует довольно большое подмножество XSLT-программ, для которых вам не нужны полные деревья DOM в памяти. –

+0

ах, хорошо спасибо. Теперь я понимаю – Karussell

2

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

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

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

+0

спасибо за ссылку. Я исследую это! – Karussell

+0

+1 JDK включает в себя StAX, так как в 1.5, насколько я помню. Гораздо удобнее использовать, чем SAX. – helpermethod

0

Самый эффективный способ объединить файлы - использовать функцию вырезания и вставки байтового уровня, предложенную 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

hmmh, но я не хочу иметь их в памяти ... просто подключите их непосредственно к диску. И я не понимаю, как это будет быстрее, чем саксофон. – Karussell

+0

с помощью sax вы делаете намного больше, чем просто набиваете их на диск, много разборок SAX - полная отработка циклов, используя VTD-XML. Я не буду судить, чтобы увидеть 10x (по крайней мере) perforamnce улучшение ... –

+0

ok. спасибо за подсказку vtd-xml. Это выглядит многообещающим (из того, что я могу прочитать на веб-сайте sourceforge). Но хотя это может быть в 100 раз быстрее. Если он занимает 100% оперативной памяти документа (или даже больше), я не могу его использовать: может быть, полученный xml не поместится даже в память. – Karussell