2009-06-09 9 views
35

में नेस्टेड तत्वों को संशोधित करना मैं स्कैला सीख रहा हूं, और मैं कुछ एक्सएमएल में नेस्टेड नोड अपडेट करना चाहता हूं। मुझे कुछ काम मिल गया है लेकिन मैं सोच रहा हूं कि यह सबसे सुंदर तरीका है या नहीं।स्कैला - xml

मैं कुछ xml है:

val InputXml : Node = 
<root> 
    <subnode> 
     <version>1</version> 
    </subnode> 
    <contents> 
     <version>1</version> 
    </contents> 
</root> 

और मुझे उपनोड में संस्करण नोड को अपडेट करना चाहते हैं, लेकिन सामग्री में नहीं है।

def updateVersion(node : Node) : Node = 
{ 
    def updateElements(seq : Seq[Node]) : Seq[Node] = 
    { 
     var subElements = for(subNode <- seq) yield 
     { 
      updateVersion(subNode) 
     } 
     subElements 
    } 

    node match 
    { 
    case <root>{ ch @ _* }</root> => 
    { 
     <root>{ updateElements(ch) }</root> 
    } 
    case <subnode>{ ch @ _* }</subnode> => 
    { 
     <subnode>{ updateElements(ch) }</subnode> 
    } 
    case <version>{ contents }</version> => 
    { 
     <version>2</version> 
    } 
    case other @ _ => 
    { 
     other 
    } 
    } 
} 

इस समारोह लेखन का एक और अधिक लधु रास्ता नहीं है:

यहाँ मेरी समारोह है?

+0

बहुत अजीब और लंबी स्वरूपण शैली ... मानक कोडिंग शैली के कुछ और समानता का उपयोग करने का सुझाव देते हैं; आपके आईडीई/संपादक में अंतर्निहित स्वरूपण होना चाहिए, आप इसके साथ शुरू कर सकते हैं। –

उत्तर

11

मुझे लगता है कि मूल तर्क अच्छा है। इस के साथ एक ही कोड (मैं कहने का साहस करेगा?) है एक और अधिक स्काला-ish स्वाद:

def updateVersion(node : Node) : Node = { 
    def updateElements(seq : Seq[Node]) : Seq[Node] = 
    for(subNode <- seq) yield updateVersion(subNode) 

    node match { 
    case <root>{ ch @ _* }</root> => <root>{ updateElements(ch) }</root> 
    case <subnode>{ ch @ _* }</subnode> => <subnode>{ updateElements(ch) }</subnode> 
    case <version>{ contents }</version> => <version>2</version> 
    case other @ _ => other 
    } 
} 

यह अधिक कॉम्पैक्ट लग रहा है (लेकिन वास्तव में है ही :))

  1. मुझे मिल गया सभी अनावश्यक कोष्ठक से छुटकारा
  2. एक ब्रैकेट की जरूरत है, तो यह एक ही पंक्ति
  3. updateElements सिर्फ एक वर परिभाषित करता है और यह रिटर्न में शुरू होता है, इसलिए मुझे लगता है कि +०१२३५१६४१० से छुटकारा मिलाऔर परिणाम सीधे

यदि आप चाहते हैं, तो आप अपडेट एलीमेंट्स से भी छुटकारा पा सकते हैं। आप अनुक्रम के सभी तत्वों के लिए अद्यतन संस्करण लागू करना चाहते हैं। यह map method है।

case <subnode>{ ch @ _* }</subnode> => <subnode>{ ch.map(updateVersion) }</subnode> 

: कि के साथ, आप लाइन

case <subnode>{ ch @ _* }</subnode> => <subnode>{ updateElements(ch) }</subnode> 

case <subnode>{ ch @ _* }</subnode> => <subnode>{ ch.map(updateVersion (_)) }</subnode> 

साथ अद्यतन संस्करण केवल 1 पैरामीटर मैं 99% यकीन है कि आप इसे छोड़ देते हैं और लिख सकता हूँ लेता है के रूप में फिर से लिखने कर सकते हैं और इसके साथ समाप्त:

def updateVersion(node : Node) : Node = node match { 
     case <root>{ ch @ _* }</root> => <root>{ ch.map(updateVersion)}</root> 
     case <subnode>{ ch @ _* }</subnode> => <subnode>{ ch.map(updateVersion) }</subnode> 
     case <version>{ contents }</version> => <version>2</version> 
     case other @ _ => other 
     } 

आपको क्या लगता है?

+0

मुझे अभी भी और मैचों में पुनरावृत्ति के बारे में परेशान है – GClaramunt

+0

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

-2

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

हालांकि एक्सएमएल के साथ इसे करने का एक अच्छा तरीका है, मैं इसे देखना चाहता हूं।

5

मैंने तब से और अधिक सीखा है और मुझे एक और जवाब में बेहतर समाधान माना जाता है। मैंने यह भी तय किया है, जैसा कि मैंने देखा है कि मैं subnode प्रतिबंध के लिए खाते में विफल रहा था।

प्रश्न के लिए धन्यवाद!एक्सएमएल से निपटने के दौरान मैंने अभी कुछ अच्छी चीजें सीखी हैं। यहां आप जो चाहते हैं वह है:

def updateVersion(node: Node): Node = { 
    def updateNodes(ns: Seq[Node], mayChange: Boolean): Seq[Node] = 
    for(subnode <- ns) yield subnode match { 
     case <version>{ _ }</version> if mayChange => <version>2</version> 
     case Elem(prefix, "subnode", attribs, scope, children @ _*) => 
     Elem(prefix, "subnode", attribs, scope, updateNodes(children, true) : _*) 
     case Elem(prefix, label, attribs, scope, children @ _*) => 
     Elem(prefix, label, attribs, scope, updateNodes(children, mayChange) : _*) 
     case other => other // preserve text 
    } 

    updateNodes(node.theSeq, false)(0) 
} 

अब, स्पष्टीकरण। पहला और अंतिम मामला विवरण स्पष्ट होना चाहिए। अंतिम एक्सएमएल के उन हिस्सों को पकड़ने के लिए मौजूद है जो तत्व नहीं हैं। या, दूसरे शब्दों में, पाठ। पहले बयान में नोट, हालांकि, ध्वज के खिलाफ परीक्षण यह इंगित करने के लिए कि version बदला जा सकता है या नहीं।

दूसरा और तीसरा मामला कथन ऑब्जेक्ट एलेम के खिलाफ पैटर्न मैचर का उपयोग करेगा। यह में सभी तत्वों को में इसके घटक भागों को तोड़ देगा। अंतिम पैरामीटर, "बच्चे @ _ *", बच्चों से किसी भी चीज की सूची में मिलेंगे। या, अधिक विशेष रूप से, एक वर्ग [नोड]। फिर हम तत्वों का पुनर्निर्माण करते हैं, जिन हिस्सों को हमने निकाला है, लेकिन सीईसी [नोड] को अपडेट नोड्स को पास करने के लिए, रिकर्सन चरण कर रहे हैं। यदि हम subnode तत्व के विरुद्ध मेल खाते हैं, तो हम ध्वज बदल सकते हैं true पर संस्करण बदल सकते हैं, संस्करण के परिवर्तन को सक्षम करते हैं।

अंतिम पंक्ति में, हम नोड से एक सेक [नोड] उत्पन्न करने के लिए node.theSeq का उपयोग करते हैं, और (0) परिणामस्वरूप सीक [नोड] का पहला तत्व प्राप्त करने के लिए। चूंकि अद्यतन नोड्स अनिवार्य रूप से एक नक्शा कार्य है (के लिए ... उपज मानचित्र में अनुवादित है), हम जानते हैं कि परिणाम में केवल एक तत्व होगा। हम false ध्वज को यह सुनिश्चित करने के लिए पास करते हैं कि version तब तक बदला जाएगा जब तक subnode तत्व पूर्वजों का नहीं होता है।

यह ऐसा करने का एक अलग तरीका होता है कि अधिक शक्तिशाली है, लेकिन थोड़ा अधिक वर्बोज़ और अस्पष्ट:

def updateVersion(node: Node): Node = { 
    def updateNodes(ns: Seq[Node], mayChange: Boolean): Seq[Node] = 
    for(subnode <- ns) yield subnode match { 
     case Elem(prefix, "version", attribs, scope, Text(_)) if mayChange => 
     Elem(prefix, "version", attribs, scope, Text("2")) 
     case Elem(prefix, "subnode", attribs, scope, children @ _*) => 
     Elem(prefix, "subnode", attribs, scope, updateNodes(children, true) : _*) 
     case Elem(prefix, label, attribs, scope, children @ _*) => 
     Elem(prefix, label, attribs, scope, updateNodes(children, mayChange) : _*) 
     case other => other // preserve text 
    } 

    updateNodes(node.theSeq, false)(0) 
} 

इस संस्करण में आप किसी भी "संस्करण" टैग, जो कुछ भी यह है बदलने की सुविधा देता उपसर्ग, गुण और गुंजाइश।

54

इस बार, और वास्तव में कोई भी सबसे उचित उत्तर नहीं दिया!

import scala.xml._ 
import scala.xml.transform._ 

object t1 extends RewriteRule { 
    override def transform(n: Node): Seq[Node] = n match { 
    case Elem(prefix, "version", attribs, scope, _*) => 
     Elem(prefix, "version", attribs, scope, Text("2")) 
    case other => other 
    } 
} 

object rt1 extends RuleTransformer(t1) 

object t2 extends RewriteRule { 
    override def transform(n: Node): Seq[Node] = n match { 
    case sn @ Elem(_, "subnode", _, _, _*) => rt1(sn) 
    case other => other 
    } 
} 

object rt2 extends RuleTransformer(t2) 

rt2(InputXml) 

अब, कुछ स्पष्टीकरण के लिए: अब जब कि मैं इसके बारे में सीखा है, हालांकि, यहां उस पर मेरी नई ले रहा है। कक्षा RewriteRule सार है। यह दो विधियों को परिभाषित करता है, जिन्हें transform कहा जाता है। उनमें से एक Node लेता है, दूसरा Node है। यह एक अमूर्त वर्ग है, इसलिए हम इसे तुरंत चालू नहीं कर सकते हैं। एक परिभाषा जोड़कर, इस मामले में transform विधियों में से एक को ओवरराइड करें, हम इसका एक अज्ञात सबक्लास बना रहे हैं। प्रत्येक रिवाइटरूल को खुद को एक ही कार्य के साथ चिंता की आवश्यकता होती है, हालांकि यह कई कर सकती है।

अगला, कक्षा RuleTransformer पैरामीटर के रूप में RewriteRule की एक चर संख्या के रूप में लेता है। यह ट्रांसफॉर्म विधि Node लेती है और के Sequence को प्रत्येक RewriteRule को लागू करने के लिए उपयोग करके इसे वापस लाती है।

दोनों वर्ग BasicTransformer से प्राप्त होते हैं, जो कुछ विधियों को परिभाषित करता है जिनके साथ किसी को उच्च स्तर पर खुद को चिंता करने की आवश्यकता नहीं होती है। यह apply विधि transform कॉल करता है, हालांकि, RuleTransformer और RewriteRule दोनों इसके साथ जुड़े वाक्य रचनात्मक चीनी का उपयोग कर सकते हैं। उदाहरण में, पूर्व करता है और बाद में नहीं करता है।

यहां हम RuleTransformer के दो स्तरों का उपयोग करते हैं, क्योंकि पहले उच्च स्तर के नोड्स पर फ़िल्टर लागू होता है, और दूसरा फिल्टर को जो भी पास करता है उसमें परिवर्तन लागू करता है।

निकालने वाला Elem भी उपयोग किया जाता है, ताकि नेमस्पेस जैसे विवरणों के साथ खुद को चिंता करने की आवश्यकता न हो या क्या विशेषताएं हैं या नहीं। यह नहीं कि तत्व version की सामग्री को पूरी तरह से हटा दिया गया है और 2 के साथ प्रतिस्थापित किया गया है। यदि आवश्यक हो तो भी इसके खिलाफ मिलान किया जा सकता है।

ध्यान दें कि निकालने वाला का अंतिम पैरामीटर _* है, और _ नहीं है। इसका मतलब है कि इन तत्वों में कई बच्चे हो सकते हैं। यदि आप * भूल जाते हैं, तो मैच विफल हो सकता है। उदाहरण में, यदि कोई सफेद जगह नहीं थी तो मैच असफल नहीं होगा। चूंकि व्हाइटस्पेस का अनुवाद Text तत्वों में किया जाता है, इसलिए subnode के तहत एक एकल सफेद स्थान असफल होने का मामला होगा।

यह कोड अन्य सुझावों से बड़ा है, लेकिन इसका एक्सएमएल की संरचना के बारे में बहुत कम ज्ञान होने का लाभ है। यह version नामक किसी भी तत्व को बदलता है - इससे कोई फर्क नहीं पड़ता कि कितने स्तर - subnode नामक तत्व, कोई नाम नहीं, नामस्थान, विशेषताओं, आदि

इसके अलावा ... अच्छा, यदि आपके पास कई बदलाव हैं, तो रिकर्सिव पैटर्न मिलान जल्दी unyielding हो जाता है। RewriteRule और RuleTransformer का उपयोग करके, आप स्कैला कोड के साथ प्रभावी ढंग से xslt फ़ाइलों को प्रतिस्थापित कर सकते हैं।

+1

शानदार, बस मुझे क्या चाहिए। संयोग से, समान विचार कक्षाओं और किमा के साथ संग्रह के लिए लागू किया जा सकता है: http://stackoverflow.com/questions/3900307/cleaner-way-to-update-nested-structures/3900498#3900498 – retronym

+0

यो। मुझे यह पसंद है, आदमी। अच्छा काम वहाँ – bharal

+0

महान स्पष्टीकरण! धन्यवाद। – drbv

12

आप लिफ्ट के CSS चयनकर्ता Transforms का उपयोग करें और लिख सकते हैं:

"subnode" #> ("version *" #> 2) 

देखें http://stable.simply.liftweb.net/#sec:CSS-Selector-Transforms

+0

एक पूर्ण उदाहरण: '(" सबनोड "#>) (इनपुटपुट)' – KajMagnus

+1

हाल के संस्करणों में 'आवेदन' को लिखा जाना होगा: "सबनोड" #> इनपुट एक्सएमएल – nafg

+0

लागू करें लघु और मीठा! –

3

Scales Xml "स्थान पर" संपादन के लिए उपकरण प्रदान करता है। बेशक अपने सभी अपरिवर्तनीय लेकिन यहाँ तराजू में समाधान है:

val subnodes = top(xml).\*("subnode"l).\*("version"l) 
val folded = foldPositions(subnodes)(p => 
    Replace(p.tree ~> "2")) 

वाक्य रचना की तरह XPath एक तराजू हस्ताक्षर सुविधा है, l स्ट्रिंग के बाद निर्दिष्ट करता है यह कोई नाम स्थान (स्थानीय केवल नाम) होना चाहिए।

foldPositions परिणामी तत्वों पर पुनरावृत्त करता है और उन्हें बदल देता है, परिणामों को एक साथ जोड़ता है।

+1

'टॉप (एक्सएमएल) \ *" सबनोड "एल \ *" संस्करण "एल' अधिक पठनीय नहीं होगा? –

1

एक दृष्टिकोण लेंस होगा (उदाहरण के लिए स्केलज़)। एक बहुत स्पष्ट प्रस्तुति के लिए http://arosien.github.io/scalaz-base-talk-201208/#slide35 देखें।

+0

बिल्कुल मेरा विचार; हाल ही में हास्केल में लेंस के बारे में पढ़ें और इस धागे को पढ़ने के तुरंत बाद घंटी बजती है :) –

+0

हालांकि, क्या आप किसी भी भाग्य से एक ठोस (छद्म) कोड उदाहरण प्रदान कर सकते हैं कि आप इस विशेष परिदृश्य में लेंस कैसे लागू करेंगे? –