2011-01-12 15 views
14

Sto osservando vari approcci per il marshalling/unmarshalling di dati tra Scala e XML, e sono interessato a ricevere feedback dalla comunità (preferibilmente basato su conoscenza/esperienza di prima mano).XML di marshalling/unmarshalling in Scala

Attualmente stiamo usando JAXB, che va bene, ma spero in una soluzione Scala pura. Sto pensando i seguenti approcci: di

  1. Usa Scala built-in strutture XML: Scala-> XML sarebbe stato facile, ma la mia ipotesi è che la direzione sarebbe abbastanza doloroso. D'altra parte, questo approccio supporta una logica di traduzione arbitraria.

  2. Associazione dati: scalaxb sembra essere un po 'immaturo in questo momento e non gestisce il nostro schema corrente, e non so di eventuali altri dati biblioteca vincolanti per Scala. Come JAXB, è richiesto un ulteriore livello di traduzione per supportare le trasformazioni coinvolte.

  3. pickler XML combinatori: La libreria fornisce GData Scala Client combinatori Pickler XML, ma l'attività recente progetto è stata bassa e io non so quale sia la situazione attuale è.

Domande:

  1. Quali sono le tue esperienze con gli approcci/librerie che ho elencato?
  2. Quali sono i relativi vantaggi e svantaggi di ciascuno?
  3. Esistono altri approcci o librerie Scala che dovrei considerare?

Edit:

ho aggiunto alcune note sulle mie prime impressioni di combinatori Pickler nella mia risposta a questa domanda, ma io sono ancora molto interessato a feedback da qualcuno che conosce realmente i diversi approcci in profondità. Quello che spero è un paragone piuttosto completo che aiuterebbe gli sviluppatori a scegliere l'approccio giusto per le loro esigenze.

+1

Se potessi inviarmi lo schema a (eed3si9n a gmail), potrei essere in grado di correggere scalaxb. –

risposta

5

Si consiglia di utilizzare le funzionalità XML incorporate di Scala. Ho appena implementato deserializzazione di una struttura del documento che assomiglia a questo:

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

Si noti che i segmenti possono essere annidati uno dentro l'altro.

Un segmento è implementato come segue:

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

per deserializzare l'XML, si esegue questa operazione:

val mySegments = topLevelSegments(bodyXML) 

... e l'implementazione di topLevelSegments si trova a poche righe di codice. Notare la ricorsione, che scava attraverso la struttura 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 } 

Sperare che aiuti.

+0

Suppongo che questo approccio non sia così pungente come mi aspettavo, ma mi chiedo quanto sia facile ridimensionare uno schema più complesso e mantenerlo nel tempo. Un vantaggio decisivo sia per l'associazione di dati che per i combinatori di pickler è che si specifica la serializzazione/deserializzazione in modo che non ci si debba preoccupare di mantenere due corpi paralleli di codice. –

+2

È vero, tuttavia, che qualsiasi altra tecnologia aggiunta al codice base comporta un sovraccarico: una sintassi da imparare, una serie di messaggi di errore da decifrare, un gruppo di utenti da unire, eventualmente un tweak di implementazione da rendere. Meno parti mobili sono, meglio è. – David

-1

Scrivere uno scala.xml.Node su una stringa non è un grosso problema. PrettyPrinter dovrebbe prendersi cura di voi ha bisogno. scala.xml.XML.save() scriverà su un file e le uscite scala.xml.XML.write() su Writer.

+2

Grazie per aver risposto, ma questo non è affatto quello che stavo cercando. Sono interessato alla conversione tra documenti XML e modelli di oggetti specifici del dominio. –

4

Per confronto, ho implementato David's example utilizzando i combinatori Pickler dalla GData Scala Client libreria:

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

Questo codice è tutto ciò che è necessario specificare entrambe le direzioni della traduzione tra Segment s e XML, mentre un importo simile del codice specifica solo una direzione della traduzione quando si utilizza la libreria XML di Scala. A mio parere, questa versione è anche più facile da capire (una volta che conosci il pickler DSL). Naturalmente, come David ha sottolineato in un commento, questo approccio richiede una dipendenza aggiuntiva e un altro DSL con cui gli sviluppatori devono avere familiarità.

Traducendo XML per segmenti è semplice come

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

e traducendo il contrario sembra

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

Per quanto riguarda la biblioteca combinatore è interessato, sembra di essere in forma decente e compila in Scala 2.8.1. La mia impressione iniziale è che alla libreria mancano alcune sottigliezze (ad esempio un combinatore oneOrMore) che potrebbero essere rimediate abbastanza facilmente. Non ho avuto il tempo di vedere quanto bene gestisce gli input sbagliati, ma finora sembra sufficiente per le mie esigenze.

+0

"uno o più" Non è questo ciò che 'rep1' fa? – soc

+0

@soc Suppongo che tu stia facendo riferimento al combinatore di parser 'rep1' nella libreria standard. Sfortunatamente, non esiste un tale combinatore nella libreria pickler XML. –

Problemi correlati