2012-05-25 12 views
8

का उपयोग कर 1000 xml फ़ाइलों को एक में कैसे विलय करें मैं कई एक्सएमएल फ़ाइलों को एक में विलय करने की कोशिश कर रहा हूं। मैंने सफलतापूर्वक डीओएम में किया है, लेकिन यह समाधान कुछ फाइलों तक ही सीमित है। जब मैं इसे एकाधिक फाइलों पर चलाता हूं> 1000 मुझे java.lang.OutOfMemoryError मिल रहा है।जावा

क्या मैं हासिल करना चाहते हैं मैं कहाँ निम्न फ़ाइलें है

फ़ाइल 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

क्या कोई कारण है कि आपको वास्तव में स्मृति में डोम रखने की आवश्यकता है? क्या आपको इस मामले में एक साधारण स्ट्रिंग कॉन्सटेनेशन की आवश्यकता है? –

+1

सरल कॉन्सटेनेशन एक्सएमएल घोषणा को बनाए रखेगा यदि प्रत्येक व्यक्तिगत एक्सएमएल फ़ाइल विलय हो रही है। बिट वास्तव में सिद्धांत रूप में मैं एक्सएमएल फाइलों का एक सरल concatenation की तलाश में हूँ। – Andra

+2

एकाधिक एक्सएमएल फाइलों को एक संग्रह में क्यों न डालें? यह एक फ़ाइल के रूप में समाप्त होता है। यदि फ़ाइल आकार या बैंडविड्थ अधिक महत्वपूर्ण है तो संकुचित/लिखने की गति महत्वपूर्ण है, तो इसे असंपीड़ित करें। –

उत्तर

8

आप स्टैक्स का उपयोग करने पर भी विचार कर सकते हैं। यहाँ कोड है कि क्या करना होगा तुम क्या चाहते हो:

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

डोम को पूरे दस्तावेज़ को स्मृति में रखने की आवश्यकता है। यदि आपको अपने टैग के साथ कोई विशेष ऑपरेशन करने की आवश्यकता नहीं है, तो मैं बस इनपुट इनपुट का उपयोग करूंगा और सभी फाइलें पढ़ूंगा। यदि आपको कुछ परिचालन करने की आवश्यकता है, तो SAX का उपयोग करें।

1

इस तरह के काम के लिए मैं सुझाव दूंगा कि डोम का उपयोग न करें, फ़ाइल सामग्री को पढ़ना और सबस्ट्रिंग बनाना सरल और पर्याप्त है।

मैं ऐसा ही कुछ सोच रहा हूँ:

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

तो अधिक स्मृति समाप्ति के लिए से बचने के लिए। उदाहरण के लिए BufferedWritter के साथ प्रत्येक एक्सएमएल निष्कर्षण के बाद मुख्य फ़ाइल में लिखें। बेहतर प्रदर्शन के लिए आप java.nio का भी उपयोग कर सकते हैं।

3

बस इसे किसी भी XML-parsing के बिना करें क्योंकि ऐसा लगता है कि यह 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 का उपयोग करके, स्मृति की केवल एक बहुत ही छोटी मात्रा का उपयोग किया जाता है, मूल रूप से लगभग एक ही तत्व किसी भी समय इनपुट से आउटपुट तक यात्रा कर रहा है, इसलिए स्मृति पदचिह्न बेहद कम है। हालांकि, sax का उपयोग करना इतना आसान नहीं है, क्योंकि डोम की तुलना में यह थोड़ा सा counterintuitive है।

स्टैक्स आज़माएं, स्वयं की कोशिश नहीं की, लेकिन यह स्टेरॉयड पर एक प्रकार का सैक्स लागू करने और उपयोग करने में आसान है, क्योंकि केवल उन सैक्स घटनाओं को प्राप्त करने के विरोध में, जिन्हें आप नियंत्रित नहीं करते हैं, आप वास्तव में आपको "स्रोत से पूछते हैं" तत्व जो आप चाहते हैं, इसलिए यह डोम और सैक्स के बीच बीच में फिट बैठता है, इसमें सैक्स के समान मेमोरी पदचिह्न है, लेकिन एक अधिक अनुकूल प्रतिमान है।

सैक्स, स्टैक्स, डोम सभी महत्वपूर्ण हैं यदि आप सही ढंग से संरक्षित करना, घोषित करना आदि ... नामस्थान और अन्य एक्सएमएल विषमताएं हैं।

हालांकि, अगर आपको केवल एक त्वरित और गंदे तरीके की आवश्यकता है, जो शायद नामस्थान भी अनुरूप होगा, तो पुराने पुराने तारों और लेखकों का उपयोग करें।

फ़ाइलवाइटर को आपके "बड़े" दस्तावेज़ की घोषणा और मूल तत्व को आउटपुट करना प्रारंभ करें। फिर लोड करें, यदि आप चाहें तो डोम का उपयोग करके, प्रत्येक फ़ाइल। उन तत्वों का चयन करें जिन्हें आप "बड़ी" फ़ाइल में समाप्त करना चाहते हैं, उन्हें एक स्ट्रिंग पर वापस क्रमबद्ध करें, और लेखक को भेजें। लेखक स्मृति की भारी मात्रा के बिना डिस्क पर फ्लश करेगा, और डोम प्रति पुनरावृत्ति केवल एक दस्तावेज़ लोड करेगा। जब तक आपके पास इनपुट पक्ष पर बहुत बड़ी फ़ाइलें न हों, या सेलफोन पर चलाने की योजना न हो, तो आपके पास बहुत सारी मेमोरी समस्याएं नहीं होनी चाहिए। यदि डोम इसे सही तरीके से क्रमबद्ध करता है, तो इसे नामस्थान घोषणाओं और समानता को संरक्षित करना चाहिए, और कोड आपके द्वारा पोस्ट किए गए कार्यों की तुलना में लाइनों का एक गुच्छा होगा।

1

मुझे लगता है कि आप जो कर रहे हैं वह मान्य है। वास्तव में बड़ी संख्या में फ़ाइलों को स्केल करने का एकमात्र तरीका स्ट्रीमिंग के साथ टेक्स्ट आधारित दृष्टिकोण का उपयोग करना है, ताकि आप पूरी चीज़ को स्मृति में कभी न रखें। लेकिन नमसते! खुशखबरी। मेमोरी इन दिनों सस्ता है, और 64 बिट जेवीएम सभी क्रोध हैं, इसलिए हो सकता है कि आपको केवल ढेर आकार में वृद्धि करना है। अपने प्रोग्राम को एक -Xms1g JVM विकल्प के साथ पुनः चलाने का प्रयास करें (1 जीबी प्रारंभिक ढेर आकार आवंटित करता है)।

मैं अपनी सभी डीओएम आवश्यकताओं के लिए XOM का भी उपयोग करता हूं। इसकी कोशिश करें। अधिक कुशल स्मृति आवश्यकताओं पर निश्चित रूप से नहीं जानते हैं, लेकिन मेरे अनुभव में तीव्रता के इसके आदेश।