Aquí hay otro enfoque que vale la pena considerar. Básicamente vamos a construir el scala.xml.Elem a partir de una cadena y haciendo uso de algunas consultas de estilo 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)
}
Primero creamos la open
y close
etiquetas, que son meras cadenas. Luego construimos children
usando alguna consulta de estilo XPath.
\\
es un operador en Elem que devuelve elementos y todas las subsecuencias del Elem.
\
es similar pero devuelve los elementos del Elem.
"_"
es el comodín.
¿Por qué no solo \
? Tuve problemas para resolver esto yo mismo en base a la documentación pero mirando XPath para Java me lleva a creer que \\
incluye todo el Elem y los niños, mientras que \
solo incluye los niños, así que si tuviéramos <parent><x/></parent> \ "parent"
no encontraríamos nada, ya que solo <x/>
es pasado.
Ahora este método no es asombroso. ¿Qué podemos hacer para que sea un poco más increíble? Será mejor que utilicemos la maravillosa clase Option
de Scala y el método 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))
}
}
Por supuesto, esto tiene la ventaja añadida de trabajar con dulzura en un solo Elem, los casos en que el padre no está presente, y un número variable de Elem proporciona como argumentos. Aquí hay una larga lista de ejemplos que ejecuté al crear este método final,
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>)