2011-10-16 66 views
5

我很好奇最好的方式來將一組包含類似 數據的xml樹組合成一個集合('union'style)。Scala:結合xml數據樹?

我確實實現了一個工作解決方案,但代碼看起來很糟糕,我有一個強烈的直覺,認爲必須有一個更好,更緊湊的方式來實現這一點。

我試圖做的是像結合東西最簡單的例子:

<fruit> <apple /> <orange /> </fruit> 

和:

<fruit> <banana /> </fruit> 

要:

<fruit> <apple/> <orange/> <banana/> </fruit> 

任何好的想法如何在scala中做一個乾淨的實現?

回答

1

val appleAndOrange : Elem = <fruit> <apple/> <orange/> </fruit> 

val banana : Elem = <fruit> <banana> </fruit> 

你可以做

val all = appleAndOrange.copy(child = appleAndOrange.child ++ banana.child) 

然而,這只是簡單地採用標籤<fruit>appleAndOrange,而忽視了一個從banana,這這裏碰巧是一樣的。同樣的,你必須決定什麼檢查你想要什麼行爲,如果他們不一樣。前綴,屬性和範圍相同。

1

這是另一種值得考慮的方法。我們本質上是要從一個字符串構建scala.xml.Elem並利用一些XPath風格的查詢。

import scala.xml._ 
def childUnion(parent: String, a: Elem, b: Elem): Elem = { 
    val open:String = "<" + parent + ">" 
    val close:String = "</" + parent + ">" 
    val children = a \\ parent \ "_" ++ b \\ parent \ "_" 
    return XML.loadString(open + children + close) 
} 

首先,我們創建了openclose標籤,這僅僅是字符串。然後我們通過使用一些XPath風格的查詢構造children

\\是Elem上的一個運算符,它返回Elem的元素和所有子序列。

\是相似的,但它返回元素的元素。

"_"是通配符。

爲什麼不只是\?我根據文檔自己找出了問題,但是查看XPath for Java讓我相信\\包含整個Elem本身和兒童,而\只包含孩子,所以如果我們有<parent><x/></parent> \ "parent",我們將找不到任何東西,因爲只有<x/>是通過。

現在這種方法並不可怕。我們可以做些什麼來讓它變得更棒?我們最好使用Scala的精彩Option類和foldLeft方法。

def childUnion(parent: String, a: Elem, b: Elem*): Option[Elem] = { 
    val parentElem = a \\ parent 

    parentElem.size match { 
     case 0 => None // no parent present 
     case _ => 
      val children = b.foldLeft(parentElem \ "_")((d,c) => (d ++ (c \\ parent \ "_"))) 
      val open:String = "<" + parent + ">" 
      val close:String = "</" + parent + ">" 
      Some(XML.loadString(open + children + close)) 
    } 
} 

當然這具有工作只是一個ELEM,情況父不存在的甜蜜額外的好處,並作爲參數提供ELEM可變數量。下面是一個很長的例子,我用這個最後的方法運行,

scala> a 
res85: scala.xml.Elem = <fruit> <apple></apple> <orange></orange> </fruit> 

scala> b 
res86: scala.xml.Elem = <fruit> <banana></banana> </fruit> 

scala> c 
res87: scala.xml.Elem = <box><fruit><apple></apple></fruit></box> 

scala> d 
res88: scala.xml.Elem = <box><nofruit></nofruit></box> 

scala> e 
res89: scala.xml.Elem = <fruit></fruit> 

scala> val f = <fruit /> 
f: scala.xml.Elem = <fruit></fruit> 

scala> childUnion("fruit", a) 
res91: Option[scala.xml.Elem] = Some(<fruit><apple></apple><orange></orange></fruit>) 

scala> childUnion("fruit", b) 
res92: Option[scala.xml.Elem] = Some(<fruit><banana></banana></fruit>) 

scala> childUnion("fruit", c) 
res93: Option[scala.xml.Elem] = Some(<fruit><apple></apple></fruit>) 

scala> childUnion("fruit", d) 
res94: Option[scala.xml.Elem] = None 

scala> childUnion("fruit", e) 
res95: Option[scala.xml.Elem] = Some(<fruit></fruit>) 

scala> childUnion("fruit", a, b) 
res96: Option[scala.xml.Elem] = Some(<fruit><apple></apple><orange></orange><banana></banana></fruit>) 

scala> childUnion("fruit", a, e) 
res97: Option[scala.xml.Elem] = Some(<fruit><apple></apple><orange></orange></fruit>) 

scala> childUnion("fruit", a, c) 
res98: Option[scala.xml.Elem] = Some(<fruit><apple></apple><orange></orange><apple></apple></fruit>) 

scala> childUnion("fruit", a, d) 
res99: Option[scala.xml.Elem] = Some(<fruit><apple></apple><orange></orange></fruit>) 

scala> childUnion("fruit", e, d) 
res100: Option[scala.xml.Elem] = Some(<fruit></fruit>) 

scala> childUnion("fruit", d, d) 
res101: Option[scala.xml.Elem] = None 

scala> childUnion("fruit", f) 
res102: Option[scala.xml.Elem] = Some(<fruit></fruit>)