Marshalling/unmarshalling XML in Scala Marshalling/unmarshalling XML in Scala xml xml

Marshalling/unmarshalling XML in Scala


I recommend using Scala's built-in XML features. I've just implemented deserialization for a document structure that looks like this:

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

Note that the segments can be nested within each other.

A segment is implemented as follows:

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

To deserialize the XML, you do this:

val mySegments = topLevelSegments(bodyXML)

...and the implementation of topLevelSegments is just a few lines of code. Note the recursion, which digs through the XML structure:

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 }

Hope that helps.


For comparison, I implemented David's example using the pickler combinators from the GData Scala Client library:

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

This code is all that is necessary to specify both directions of the translation between Segments and XML, whereas a similar amount of code specifies only one direction of the translation when using the Scala XML library. In my opinion, this version is also easier to understand (once you know the pickler DSL). Of course, as David pointed out in a comment, this approach requires an additional dependency and another DSL that developers have to be familiar with.

Translating XML to Segments is as simple as

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

and translating the other way looks like

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

As far as the combinator library is concerned, it seems to be in decent shape and compiles in Scala 2.8.1. My initial impression is that the library is missing a few niceties (e.g. a oneOrMore combinator) that could be remedied fairly easily. I haven't had time to see how well it handles bad input, but so far it looks sufficient for my needs.


Writing a scala.xml.Node to a string isn't a big deal. PrettyPrinter should take care of you needs. scala.xml.XML.save() will write to a file and scala.xml.XML.write() outputs to a Writer.