2012-05-25 2 views
8

많은 xml 파일을 하나로 병합하려고합니다. DOM에서 성공적으로 수행했지만이 솔루션은 몇 개의 파일로 제한됩니다. 여러 파일> 1000에서 실행하면 java.lang.OutOfMemoryError가 발생합니다. 나는 다음과 같은 파일이 어디> 1000 xml 파일을 Java를 사용하여 하나로 병합하는 방법

는 내가 달성하고자하는 것은

파일 1 :

<root> 
.... 
</root> 

파일 2 :

<root> 
...... 
</root> 

파일 N :

<root> 
.... 
</root> 

결과 : 출력 :

<rootSet> 
<root> 
.... 
</root> 
<root> 
.... 
</root> 
<root> 
.... 
</root> 
</rootSet> 

이 내 현재의 구현 :

DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); 
    DocumentBuilder docBuilder = docFactory.newDocumentBuilder(); 
    Document doc = docBuilder.newDocument(); 
    Element rootSetElement = doc.createElement("rootSet"); 
    Node rootSetNode = doc.appendChild(rootSetElement); 
    Element creationElement = doc.createElement("creationDate"); 
    rootSetNode.appendChild(creationElement); 
    creationElement.setTextContent(dateString); 
    File dir = new File("/tmp/rootFiles"); 
    String[] files = dir.list(); 
    if (files == null) { 
     System.out.println("No roots to merge!"); 
    } else { 
     Document rootDocument; 
      for (int i=0; i<files.length; i++) { 
         File filename = new File(dir+"/"+files[i]);   
       rootDocument = docBuilder.parse(filename); 
       Node tempDoc = doc.importNode((Node) Document.getElementsByTagName("root").item(0), true); 
       rootSetNode.appendChild(tempDoc); 
     } 
    } 

내가 XSLT, 색소폰과 많은 실험을했다,하지만 난 뭔가를 놓치고 유지하는 것 같다. 어떤 도움을 주시면 감사하겠습니다.

+4

실제로 DOM을 메모리에 보관해야하는 이유가 있습니까? 이 경우 간단한 문자열 연결 이상을 필요로합니까? –

+1

단순 연결은 각 개별 xml 파일이 병합되는 경우 xml 선언을 유지합니다. 사실 원칙적으로 xml 파일의 간단한 연결을 찾고 있습니다. – Andra

+2

왜 여러 XML 파일을 하나의 아카이브에 저장하지 않습니까? 그것은 하나의 파일로 끝납니다. 읽기/쓰기 속도가 중요하고 파일 크기 나 대역폭이 더 중요한 경우 압축하면 압축되지 않습니다. –

답변

8

StAX 사용을 고려해 볼 수도 있습니다. 여기에 당신이 원하는 것을 할 것입니다 코드입니다 :

import java.io.File; 
import java.io.FileWriter; 
import java.io.Writer; 

import javax.xml.stream.XMLEventFactory; 
import javax.xml.stream.XMLEventReader; 
import javax.xml.stream.XMLEventWriter; 
import javax.xml.stream.XMLInputFactory; 
import javax.xml.stream.XMLOutputFactory; 
import javax.xml.stream.events.XMLEvent; 
import javax.xml.transform.stream.StreamSource; 

public class XMLConcat { 
    public static void main(String[] args) throws Throwable { 
     File dir = new File("/tmp/rootFiles"); 
     File[] rootFiles = dir.listFiles(); 

     Writer outputWriter = new FileWriter("/tmp/mergedFile.xml"); 
     XMLOutputFactory xmlOutFactory = XMLOutputFactory.newFactory(); 
     XMLEventWriter xmlEventWriter = xmlOutFactory.createXMLEventWriter(outputWriter); 
     XMLEventFactory xmlEventFactory = XMLEventFactory.newFactory(); 

     xmlEventWriter.add(xmlEventFactory.createStartDocument()); 
     xmlEventWriter.add(xmlEventFactory.createStartElement("", null, "rootSet")); 

     XMLInputFactory xmlInFactory = XMLInputFactory.newFactory(); 
     for (File rootFile : rootFiles) { 
      XMLEventReader xmlEventReader = xmlInFactory.createXMLEventReader(new StreamSource(rootFile)); 
      XMLEvent event = xmlEventReader.nextEvent(); 
      // Skip ahead in the input to the opening document element 
      while (event.getEventType() != XMLEvent.START_ELEMENT) { 
       event = xmlEventReader.nextEvent(); 
      } 

      do { 
       xmlEventWriter.add(event); 
       event = xmlEventReader.nextEvent(); 
      } while (event.getEventType() != XMLEvent.END_DOCUMENT); 
      xmlEventReader.close(); 
     } 

     xmlEventWriter.add(xmlEventFactory.createEndElement("", null, "rootSet")); 
     xmlEventWriter.add(xmlEventFactory.createEndDocument()); 

     xmlEventWriter.close(); 
     outputWriter.close(); 
    } 
} 

한 약간의주의가이 API는 <foo></foo><foo/>을 변경하는 빈 태그와 혼란에 보인다는 것이다.

2

DOM은 전체 문서를 메모리에 보관해야합니다. 태그를 사용하여 특별한 작업을 수행 할 필요가 없다면, 단순히 InputStream을 사용하고 모든 파일을 읽습니다. 몇 가지 작업을 수행해야하는 경우 SAX를 사용하십시오.

1

이런 종류의 작업에서는 DOM을 사용하지 말고 파일 내용을 읽고 하위 문자열을 만드는 것이 더 간단하고 충분하다고 제안합니다.

나는 그런 일을 생각 해요 :

String rootContent = document.substring(document.indexOf("<root>"), document.lastIndexOf("</root>")+7); 

그런 다음 많은 메모리 완성을 피하기 위해. 예를 들어 BufferedWritter을 사용하여 매 xml 추출 후 기본 파일에 씁니다. 더 나은 성능을 위해 java.nio을 사용할 수도 있습니다.

3

xml 파싱을 실제로 수행 할 필요가없는 것처럼 보이기 때문에 xml 파싱을하지 않고 그냥 수행하십시오. 효율성을 위해

은 같은 것을 할 :

File dir = new File("/tmp/rootFiles"); 
String[] files = dir.list(); 
if (files == null) { 
    System.out.println("No roots to merge!"); 
} else { 
     try (FileChannel output = new FileOutputStream("output").getChannel()) { 
      ByteBuffer buff = ByteBuffer.allocate(32); 
      buff.put("<rootSet>\n".getBytes()); // specify encoding too 
      buff.flip(); 
      output.write(buff); 
      buff.clear(); 
      for (String file : files) { 
       try (FileChannel in = new FileInputStream(new File(dir, file).getChannel()) { 
        in.transferTo(0, 1 << 24, output); 
       } catch (IOException e) { 
        e.printStackTrace(); 
       } 
      } 
      buff.put("</rootSet>\n".getBytes()); // specify encoding too 
      buff.flip(); 
      output.write(buff); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
2

돔은 많은 메모리를 소비한다. 당신에게는 다음과 같은 대안이 있습니다.

SAX를 사용하는 것이 가장 좋습니다. 색소폰을 사용하면 매우 적은 양의 메모리 만 사용되므로 기본적으로 거의 모든 요소가 입력에서 출력으로 이동하므로 메모리 사용량이 매우 적습니다. 그러나, 색소폰을 사용하는 것은 그렇게 단순하지 않습니다. 왜냐하면 dom과 비교할 때 약간 직관력이 떨어집니다.

Stax를 사용해보십시오. 직접 시도하지는 않았지만 구현하기 쉽고 스테로이드를 사용하는 일종의 색소폰입니다. 통제하지 못하는 색소폰 이벤트를받는 것과는 대조적으로, 실제로 소스에 물어 보며 당신이 원하는 요소들, 그래서 dom과 sax 사이의 중간에 위치하며, 색소폰과 비슷한 메모리 풋 프린트를 가졌지 만 더 친숙한 패러다임입니다.

Sax, stax, dom은 네임 스페이스와 기타 XML oddities를 올바르게 보존하고 선언하려는 경우 모두 중요합니다.

그러나 네임 스페이스를 준수하는 빠른 방법과 더러운 방법이 필요한 경우 일반 오래된 문자열과 작성기를 사용하십시오.

"큰"문서의 선언과 루트 요소를 FileWriter에 출력하기 시작합니다. 원하는 경우 dom을 사용하여 각 단일 파일을로드합니다. "큰"파일에서 끝내고, 문자열로 다시 직렬화하고, 작성자에게 보낼 요소를 선택하십시오. 작가는 엄청난 양의 메모리를 사용하지 않고 디스크로 플러시 할 것이고 dom은 반복마다 하나의 문서 만로드 할 것입니다. 입력면에 매우 큰 파일이 있거나 휴대 전화로 파일을 실행하려고 계획하지 않는 한 많은 메모리 문제가 발생하지 않아야합니다. dom이 올바르게 직렬화하면 네임 스페이스 선언 등을 보존해야하며 코드는 게시 한 코드보다 훨씬 많은 줄이됩니다.

1

당신이하고있는 일이 유효하다고 생각합니다. 실제로 엄청난 수의 파일로 확장 할 수있는 유일한 방법은 스트리밍 방식의 텍스트 기반 접근 방식을 사용하는 것입니다. 따라서 모든 것을 메모리에 보관하지 마십시오. 그러나, 헤이! 좋은 소식. 요즘엔 메모리가 저렴하고 64 비트 JVM이 대세다. 그래서 힙 크기를 늘리면된다. 프로그램을 -Xms1g JVM 옵션 (1Gb 초기 힙 크기 할당)으로 다시 실행하십시오.

내 모든 DOM 요구 사항에 대해 XOM을 사용하는 경향이 있습니다. 그것을 줘. 훨씬 더 효율적입니다. 기억 요구 사항에 대해서는 확실히 알지 못하지만 내 경험상 그 순서는 더 빠릅니다.

관련 문제