2011-10-16 11 views
5

Sono curioso il modo migliore per combinare una serie di alberi XML contenente simili dati in un unico set ('unione' stile).Scala: combina alberi di dati xml?

ho fatto implementare una soluzione di lavoro ma il codice sembra male e ho una forte sensazione viscerale che ci deve essere un modo molto più bello e compatto di attuazione del presente.

Quello che sto cercando di fare è nel caso più semplice combinando qualcosa di simile:

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

e:

<fruit> <banana /> </fruit> 

A:

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

Delle buone idee come fare una chiara implementazione di questo in scala?

risposta

1

con

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

e

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

si può fare

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

Tuttavia, questo richiede semplicemente l'etichetta <fruit> da appleAndOrange, e ignorare quello da banana, che qui capita di essere lo stesso. Lo stesso per te devi decidere quali controlli vuoi e quale comportamento, se non sono gli stessi. Lo stesso per prefissi, attributi e ambiti.

1

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