Ecco un altro approccio che vale la pena considerare. Stiamo essenzialmente costruendo scala.xml.Elem da una stringa e facendo uso di alcune query in stile 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)
}
In primo luogo abbiamo creato le open
e close
tag, che sono solo stringhe. Quindi costruiamo children
utilizzando una query in stile XPath.
\\
è un operatore su Elem che restituisce elementi e tutti sottosequenze della Elem.
\
è simile ma restituisce gli elementi del Elem.
"_"
è il jolly.
Perché non solo \
? Ho avuto difficoltà a capire tutto da solo sulla base della documentazione, ma guardando XPath per Java mi porta a credere che \\
includa l'intero Elem stesso e i bambini mentre \
include solo i bambini, quindi se avessimo <parent><x/></parent> \ "parent"
non troveremmo nulla dal momento che solo <x/>
è passato.
Ora, questo metodo non è impressionante. Cosa possiamo fare per renderlo un po 'più fantastico? Faremmo meglio a utilizzare la meravigliosa classe Option
di Scala e il metodo 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))
}
}
Questo ovviamente ha il vantaggio aggiunto di calciare di lavorare su una sola Elem, qualora il genitore non è presente, e un numero variabile di Elem forniti come argomenti. Ecco un lungo elenco di esempi che ho eseguito durante l'elaborazione di questo metodo finale,
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>)