2011-10-16 21 views
5

Tengo curiosidad por la mejor manera de combinar un conjunto de árboles xml que contienen datos similares en un único conjunto (estilo 'unión').Scala: ¿combinar árboles xml de datos?

Implementé una solución de trabajo pero el código se ve mal y tengo una fuerte intuición de que debe haber una forma mucho más agradable y compacta de implementar esto.

Lo que estoy tratando de hacer es en el caso más sencillo combinar algo como:

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

y:

<fruit> <banana /> </fruit> 

Para:

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

Buenas ideas de cómo hacer una implementación limpia de esto en scala?

Respuesta

1

con

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

y

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

que puede hacer

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

Sin embargo, esto simplemente toma el la etiqueta <fruit> de appleAndOrange, e ignoran el de banana, que aquí pasa a ser lo mismo. Lo mismo para usted Tiene que decidir qué controles desea y qué comportamiento, si no son lo mismo. Lo mismo para prefijos, atributos y ámbitos.

1

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>)