2011-10-16 4 views
5

Je suis curieux de la meilleure façon de combiner un ensemble d'arbres xml contenant des données similaires à un ensemble unique (style 'union').Scala: Combiner des arbres xml de données?

J'ai implémenté une solution de travail mais le code semble mauvais et j'ai une intuition forte qu'il doit y avoir une manière beaucoup plus agréable et compacte de mettant en œuvre ceci.

Ce que je suis en train de faire est dans le cas le plus simple combinaison de quelque chose comme:

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

et:

<fruit> <banana /> </fruit> 

Pour:

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

Toutes les bonnes idées comment faire une mise en œuvre propre de cela dans Scala?

Répondre

1

avec

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

et

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

vous pouvez faire

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

Cependant, cela prend simplement l'étiquette <fruit> de appleAndOrange, et ignorer celle de banana, qui il arrive que ce soit la même chose. Idem pour Vous devez décider quels contrôles vous voulez et quel comportement, s'ils ne sont pas identiques. Idem pour les préfixes, les attributs et les étendues.

1

Voici une autre approche qui mérite d'être considérée. Nous allons essentiellement construire le fichier scala.xml.Elem à partir d'une chaîne et utiliser des requêtes de style 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) 
} 

Tout d'abord nous avons créé les open et close balises, qui ne sont que des chaînes. Ensuite, nous construisons children en utilisant une requête de style XPath.

\\ est un opérateur sur Elem qui retourne des éléments et toutes les sous-séquences de l'Elem.

\ est similaire mais il renvoie les éléments de l'Elem.

"_" est le caractère générique.

Pourquoi ne pas simplement \? J'ai eu du mal à comprendre cela sur la base de la documentation, mais en regardant XPath for Java, je pense que \\ comprend tout l'Elem lui-même et les enfants alors que \ ne comprend que les enfants, donc si nous avions <parent><x/></parent> \ "parent" nous ne trouverions rien puisque seulement est passé.

Maintenant, cette méthode n'est pas géniale. Que pouvons-nous faire pour le rendre un peu plus génial? Nous ferions mieux d'utiliser la classe Option de Scala et la méthode 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)) 
    } 
} 

Bien sûr, a l'avantage de travailler doucement ajouté sur un seul Elem, les cas où le parent est absent, et un nombre variable de Elem comme argument. Voici une longue liste d'exemples que j'ai courus en présentant cette dernière méthode,

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