2011-01-12 2 views
14

Je suis en train d'examiner différentes approches pour le marshalling/unmarshalling de données entre Scala et XML, et je suis intéressé à obtenir des commentaires de la communauté (de préférence basé sur des connaissances/expériences de première main).Marshalling/unmarshalling XML dans Scala

Nous utilisons actuellement JAXB, ce qui est bien, mais j'espère une solution Scala pure. Je considérais les approches suivantes:

  1. Utilisez Scala de intégré dans des installations XML: Scala-> XML serait facile, mais je pense que l'autre direction serait assez douloureuse. D'un autre côté, cette approche supporte une logique de traduction arbitraire.

  2. liaison de données: scalaxb semble être un peu immature au moment et ne gère pas notre schéma actuel, et je ne sais pas d'autres données pour reliure Scala. Comme JAXB, une couche de traduction supplémentaire est nécessaire pour prendre en charge les transformations impliquées.

  3. XML Pickler Combinators: La bibliothèque GData Scala Client fournit combinators XML Pickler, mais l'activité récente du projet a été faible et je ne sais pas ce que l'état actuel.

Questions:

  1. Quelles sont vos expériences avec les approches/bibliothèques J'ai énuméré?
  2. Quels sont les avantages et les inconvénients de chacun?
  3. Existe-t-il d'autres approches ou bibliothèques Scala que je devrais envisager?

Edit:

j'ai ajouté à ma réponse à cette question quelques notes sur mes impressions premières de Pickler combinateurs, mais je suis toujours très intéressé par les commentaires de quelqu'un qui connaît réellement les différentes approches en profondeur. Ce que j'espère, c'est une comparaison assez complète qui aiderait les développeurs à choisir la bonne approche pour leurs besoins.

+1

Si vous pouviez m'envoyer le schéma à (eed3si9n at gmail), je pourrais être en mesure de corriger scalaxb. –

Répondre

5

Je recommande d'utiliser les fonctionnalités XML intégrées de Scala. Je viens pour une désérialisation mis en œuvre la structure du document qui se présente comme suit:

val bodyXML = <body><segment uri="foo"><segment uri="bar" /></segment></body> 

Notez que les segments peuvent être imbriqués les uns dans les autres.

Un segment est mis en œuvre comme suit:

case class Segment(uri: String, children: Seq[Segment]) 

désérialiser le XML, vous faites ceci:

val mySegments = topLevelSegments(bodyXML) 

... et la mise en œuvre de topLevelSegments est à seulement quelques lignes de code. Notez la récursivité, qui creuse à travers la structure XML:

def topLevelSegments(bodyXML: Node): Seq[Segment] = 
    (bodyXML \ "segment") map { nodeToSegment } 

def nodeToSegment = (n: Node) => Segment((n \ "@uri")(0) text, childrenOf(n)) 

def childrenOf(n: Node): Seq[Segment] = (n \ "segment") map { nodeToSegment } 

Espérons que cela aide.

+0

Je suppose que cette approche n'est pas aussi poilue que je le pensais, mais je me demande à quel point il est facile à l'échelle d'un schéma plus complexe et de maintenir avec le temps. Un avantage certain des combinateurs de liaison de données et de sélection est que vous spécifiez simultanément la sérialisation/désérialisation afin que vous n'ayez pas à vous préoccuper de la maintenance de deux corps de code parallèles. –

+2

Il est vrai, cependant, que toute technologie supplémentaire que vous mélangez dans votre base de code entraîne une surcharge: une syntaxe à apprendre, un ensemble de messages d'erreur à déchiffrer, un groupe d'utilisateurs à rejoindre, éventuellement un réglage à déployer. Moins il y a de «pièces mobiles», mieux c'est. – David

-1

L'écriture d'un noeud scala.xml.Node sur une chaîne n'est pas une grosse affaire. PrettyPrinter devrait prendre soin de vos besoins. scala.xml.XML.save() va écrire dans un fichier et scala.xml.XML.write() sorties à un Writer.

+2

Merci d'avoir répondu, mais ce n'est pas du tout ce que je cherchais. Je suis intéressé par la conversion entre les documents XML et les modèles d'objets spécifiques au domaine. –

4

À titre de comparaison, je mis en œuvre David's example en utilisant les combinateurs Pickler de la bibliothèque GData Scala Client:

def segment: Pickler[Segment] = 
    wrap(elem("segment", 
      attr("uri", text) 
      ~ rep(segment))) { // rep = zero or more repetitions 
     // convert (uri ~ children) to Segment(uri, children), for unpickling 
     Segment.apply 
    } { 
     // convert Segment to (uri ~ children), for pickling 
     (s: Segment) => new ~(s.uri, s.children toList) 
    } 

def body = elem("body", rep(segment)) 

case class Segment(uri: String, children: List[Segment]) 

Ce code est tout ce qui est nécessaire de spécifier les deux sens de la traduction entre Segment s et XML, alors qu'un montant similaire de code ne spécifie qu'une seule direction de la traduction lors de l'utilisation de la bibliothèque Scala XML. À mon avis, cette version est également plus facile à comprendre (une fois que vous connaissez le pickler DSL). Bien sûr, comme David l'a souligné dans un commentaire, cette approche nécessite une dépendance supplémentaire et un autre DSL que les développeurs doivent connaître.

Traduire XML à des segments est aussi simple que

body.unpickle(LinearStore.fromFile(filename)) // returns a PicklerResult[List[Segment]] 

et la traduction dans l'autre sens ressemble

xml.XML.save(filename, body.pickle(segments, PlainOutputStore.empty).rootNode) 

En ce qui concerne la bibliothèque combinateur est concerné, il semble être en bon état et compile dans Scala 2.8.1. Mon impression initiale est que la bibliothèque manque quelques subtilités (par exemple un combinateur oneOrMore) qui pourraient être corrigées assez facilement. Je n'ai pas eu le temps de voir à quel point il gère les mauvaises entrées, mais jusqu'à présent cela semble suffisant pour mes besoins.

+0

"un ou plusieurs" N'est-ce pas ce que fait 'rep1'? – soc

+0

@soc Je suppose que vous faites référence au combinateur d'analyseurs 'rep1' dans la bibliothèque standard. Malheureusement, il n'y a pas de tel combinateur dans la bibliothèque de sélecteur XML. –