2011-12-15 4 views
8

मुझे XML दस्तावेज़ों को संसाधित करने की आवश्यकता है जिनमें बहुत से स्वतंत्र रिकॉर्ड शामिल हैं, उदा।एक बड़े एक्सएमएल दस्तावेज़ से स्ट्रीमिंग इटरेटर [नोड] कैसे प्राप्त करें?

<employees> 
    <employee> 
     <firstName>Kermit</firstName> 
     <lastName>Frog</lastName> 
     <role>Singer</role> 
    </employee> 
    <employee> 
     <firstName>Oscar</firstName> 
     <lastName>Grouch</lastName> 
     <role>Garbageman</role> 
    </employee> 
    ... 
</employees> 

कुछ मामलों में ये केवल बड़ी फ़ाइलें हैं, लेकिन अन्य में वे स्ट्रीमिंग स्रोत से आ सकते हैं।

मैं सिर्फ scala.xml.XmlLoader.load() नहीं कर सकता क्योंकि यह पूरे दस्तावेज़ को मेमोरी में नहीं रखना चाहता (या इनपुट स्ट्रीम को बंद करने के लिए प्रतीक्षा करें), जब मुझे केवल काम करने की ज़रूरत है एक समय में एक रिकॉर्ड। मुझे पता है कि मैं XmlEventReader का उपयोग XmlEvents के अनुक्रम के रूप में इनपुट स्ट्रीम करने के लिए कर सकता हूं। हालांकि scala.xml.Node से काम करने के लिए ये बहुत कम सुविधाजनक हैं।

तो मैं एक आलसी इटरेटर [नोड] जबकि नियंत्रण में स्मृति उपयोग रखते हुए, किसी भी तरह इस से बाहर निकलने के क्रम में, सुविधाजनक स्काला सिंटैक्स का उपयोग प्रत्येक व्यक्ति के रिकॉर्ड पर संचालित करने के लिए करना चाहते हैं।

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

उत्तर

8

आप XMLEventReaderConstructingParser के माध्यम से उपयोग किए गए अंतर्निहित पार्सर का उपयोग कर सकते हैं और कॉलबैक के साथ शीर्ष स्तर के नीचे अपने कर्मचारी नोड्स को संसाधित कर सकते हैं। तुम बस के रूप में जल्द कार्रवाई की के रूप में डेटा को त्यागकर सावधान रहना होगा:

import scala.xml._ 

def processSource[T](input: Source)(f: NodeSeq => T) { 
    new scala.xml.parsing.ConstructingParser(input, false) { 
    nextch // initialize per documentation 
    document // trigger parsing by requesting document 

    var depth = 0 // track depth 

    override def elemStart(pos: Int, pre: String, label: String, 
     attrs: MetaData, scope: NamespaceBinding) { 
     super.elemStart(pos, pre, label, attrs, scope) 
     depth += 1 
    } 
    override def elemEnd(pos: Int, pre: String, label: String) { 
     depth -= 1 
     super.elemEnd(pos, pre, label) 
    } 
    override def elem(pos: Int, pre: String, label: String, attrs: MetaData, 
     pscope: NamespaceBinding, nodes: NodeSeq): NodeSeq = { 
     val node = super.elem(pos, pre, label, attrs, pscope, nodes) 
     depth match { 
     case 1 => <dummy/> // dummy final roll up 
     case 2 => f(node); NodeSeq.Empty // process and discard employee nodes 
     case _ => node // roll up other nodes 
     } 
    } 
    } 
} 

तो फिर तुम निरंतर स्मृति में दूसरे स्तर पर प्रत्येक नोड पर कार्रवाई करने के इस तरह का उपयोग करें (दूसरे स्तर पर नोड्स संभालने एक नहीं मिल रहा है बच्चों की मनमाना संख्या):

processSource(src){ node => 
    // process here 
    println(node) 
} 

लाभ XMLEventReader की तुलना में है कि आप दो धागे का उपयोग नहीं करते है। इसके अलावा आपको अपने प्रस्तावित समाधान की तुलना में नोड को दो बार पार्स करने की आवश्यकता नहीं है। दोष यह है कि यह ConstructingParser की आंतरिक कार्यप्रणाली पर निर्भर करता है।

+0

शानदार! यह बहुत अच्छा काम करता है। इस जनरेटर-स्टाइल चीज़ से इटरेटर तक पहुंचने के लिए बहुत मुश्किल नहीं है; मेरा दूसरा जवाब देखें। बहुत धन्यवाद! –

5

एक TraversableOnce[Node] को huynhjl के जनरेटर समाधान से प्राप्त करने के लिए, this trick का उपयोग करें:

def generatorToTraversable[T](func: (T => Unit) => Unit) = 
    new Traversable[T] { 
    def foreach[X](f: T => X) { 
     func(f(_)) 
    } 
    } 

def firstLevelNodes(input: Source): TraversableOnce[Node] = 
    generatorToTraversable(processSource(input)) 

generatorToTraversable का परिणाम है नहीं traversable एक बार (भले ही एक नया ConstructingParser प्रत्येक foreach फोन पर instantiated है) की तुलना में अधिक है क्योंकि इनपुट धारा एक स्रोत है, जो एक इटरेटर है। हम Traversable.isTraversableAgain को ओवरराइड नहीं कर सकते, हालांकि, क्योंकि यह अंतिम है।

वास्तव में हम इसे एक इटरेटर लौटकर इसे लागू करना चाहते हैं। हालांकि, दोनों Traversable.toIterator और Traversable.view.toIterator एक मध्यवर्ती स्ट्रीम है, जो सभी प्रविष्टियों (इस अभ्यास का पूरा उद्देश्य को हराने) कैश होगा। ओह अच्छा; यदि स्ट्रीम को दो बार एक्सेस किया जाता है तो मैं स्ट्रीम को अपवाद फेंक दूंगा।

यह भी ध्यान दें कि पूरी चीज थ्रेड सुरक्षित नहीं है।

इस कोड को महान चलाता है, और मैं, का मानना ​​है कि समग्र समाधान दोनों आलसी और कैशिंग (इसलिए निरंतर स्मृति) नहीं होने के लिए हालांकि मैं अभी तक एक बड़ी इनपुट पर इसे करने की कोशिश नहीं की है।

+0

मुझे इस अद्भुत चाल के बारे में पता नहीं था! – huynhjl

संबंधित मुद्दे