2009-07-07 9 views
5

के साथ एक्सएमएल नेमस्पेस बदलना मैं scala.xml.XML.loadFile() विधि के माध्यम से फ़ाइल से एक्सएमएल फ़ाइल लोड करने के लिए स्कैला का उपयोग कर रहा हूं। जिन दस्तावेज़ों के साथ मैं काम कर रहा हूं उनमें नामस्थान पहले से ही परिभाषित हैं और मैं स्काला का उपयोग करके नामस्थान को किसी अन्य चीज़ में बदलना चाहता हूं। उदाहरण के लिए, "दस्तावेज़" के उपसर्ग के साथ एक दस्तावेज़ में "http://foo.com/a" का xmlns है - मैं दस्तावेज़ के लिए नामस्थान और उपसर्ग को क्रमशः "http://foo.com/b" और "b" में बदलना चाहता हूं।स्कैला

आसान लगता है और मुझे लगता है कि मुझे यहां कुछ स्पष्ट याद आ रहा है। मुझे संदर्भ से loadFile() विधि से नामस्थान प्राप्त करने में कोई समस्या नहीं है।

उत्तर

9

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

नीचे दिया गया कार्य केवल एक विशेष यूआरआई और उपसर्ग को बदल देगा, और यह सभी नामस्थानों की जांच करेगा, यह देखने के लिए कि क्या उपसर्ग या यूआरआई को बदलने की जरूरत है या नहीं। यह एक उपसर्ग या एक दूसरे से स्वतंत्र यूआरआई को बदल देगा, जो शायद वांछित नहीं हो सकता है। हालांकि, यह एक बड़ा सौदा तय नहीं है।

बाकी के लिए, एक्सएमएल के प्रत्येक भाग में रिकर्स करने के लिए सिर्फ एलेम पर पैटर्न मिलान होता है। आह, हाँ, यह तत्वों के उपसर्ग भी बदलता है। दोबारा, अगर वह नहीं चाहता है, तो इसे बदलना आसान है।

कोड मानता है कि एक्सएमएल के "अन्य" हिस्सों में पुन: प्रयास करने की कोई आवश्यकता नहीं है - शेष आमतौर पर पाठ तत्व होंगे। साथ ही, यह मानता है कि कहीं और नामस्थान नहीं है। मैं एक्सएमएल पर कोई विशेषज्ञ नहीं हूं, इसलिए मैं दोनों गिनती पर गलत हो सकता हूं। एक बार और, इसे बदलना आसान होना चाहिए - बस पैटर्न का पालन करें।

def changeNS(el: Elem, 
      oldURI: String, newURI: String, 
      oldPrefix: String, newPrefix: String): Elem = { 
    def replace(what: String, before: String, after: String): String = 
    if (what == before) after else what 

    def fixScope(ns: NamespaceBinding): NamespaceBinding = 
    if(ns == TopScope) 
     TopScope 
    else new NamespaceBinding(replace(ns.prefix, oldPrefix, newPrefix), 
           replace(ns.uri, oldURI, newURI), 
           fixScope(ns.parent)) 

    def fixSeq(ns: Seq[Node]): Seq[Node] = for(node <- ns) yield node match { 
    case Elem(prefix, label, attribs, scope, children @ _*) => 
     Elem(replace(prefix, oldPrefix, newPrefix), 
      label, 
      attribs, 
      fixScope(scope), 
      fixSeq(children) : _*) 
    case other => other 
    } 
    fixSeq(el.theSeq)(0).asInstanceOf[Elem] 
} 

हालांकि यह एक अप्रत्याशित परिणाम उत्पन्न करता है। दायरे सभी तत्वों में जोड़ा जा रहा है। ऐसा इसलिए है क्योंकि नेमस्पेस बाइंडिंग एक समान विधि को परिभाषित नहीं करता है, इस प्रकार संदर्भ समानता का उपयोग करता है। मैंने इसके लिए टिकट खोला है, 2138, जो पहले ही बंद कर दिया गया है, इसलिए स्कैला 2.8 में यह समस्या नहीं होगी।

इस बीच, निम्न कोड ठीक से काम करेगा। यह नामस्थानों का कैश रखता है। यह नामकरण से पहले एक सूची में नेमस्पेस बाइंडिंग को भी विघटित करता है।

def changeNS(el: Elem, 
      oldURI: String, newURI: String, 
      oldPrefix: String, newPrefix: String): Elem = { 
    val namespaces = scala.collection.mutable.Map.empty[List[(String, String)],NamespaceBinding] 

    def replace(what: String, before: String, after: String): String = 
    if (what == before) after else what 

    def unfoldNS(ns: NamespaceBinding): List[(String, String)] = ns match { 
    case TopScope => Nil 
    case _ => (ns.prefix, ns.uri) :: unfoldNS(ns.parent) 
    } 

    def foldNS(unfoldedNS: List[(String, String)]): NamespaceBinding = unfoldedNS match { 
    case knownNS if namespaces.isDefinedAt(knownNS) => namespaces(knownNS) 
    case (prefix, uri) :: tail => 
     val newNS = new NamespaceBinding(prefix, uri, foldNS(tail)) 
     namespaces(unfoldedNS) = newNS 
     newNS 
    case Nil => TopScope 
    } 

    def fixScope(ns: NamespaceBinding): NamespaceBinding = 
    if(ns == TopScope) 
     ns 
    else { 
     val unfoldedNS = unfoldNS(ns) 
     val fixedNS = for((prefix, uri) <- unfoldedNS) 
        yield (replace(prefix, oldPrefix, newPrefix), replace(uri, oldURI, newURI)) 

     if(!namespaces.isDefinedAt(unfoldedNS)) 
     namespaces(unfoldedNS) = ns // Save for future use 

     if(fixedNS == unfoldedNS) 
     ns 
     else 
     foldNS(fixedNS) 
    } 

    def fixSeq(ns: Seq[Node]): Seq[Node] = for(node <- ns) yield node match { 
    case Elem(prefix, label, attribs, scope, children @ _*) => 
     Elem(replace(prefix, oldPrefix, newPrefix), 
      label, 
      attribs, 
      fixScope(scope), 
      fixSeq(children) : _*) 
    case other => other 
    } 
    fixSeq(el.theSeq)(0).asInstanceOf[Elem] 
} 
0

यहां मामूली बग। गुणों के योग्य नाम भी हो सकते हैं। आपको उनको भी जांचना होगा।