Decoding nodes as arbitrary types

We’ve seen in a previous tutorial how to extract primitive types, tuples and case classes from XML documents. Sometimes however, none of these fit our requirements. kantan.xpath provides support for extracting arbitrary types, which works almost exactly the same as case classes.

In order to show how that works, we’ll first need some sample XML data, which we’ll get from this project’s resources:

val rawData: java.net.URL = getClass.getResource("/simple.xml")

This is what we’re working with:

scala.io.Source.fromURL(rawData).mkString
// res0: String = """<root>
//     <element id="1" enabled="true"/>
//     <element id="2" enabled="false"/>
//     <element id="3" enabled="true"/>
//     <element id="4" enabled="false"/>
// </root>"""

We’ll be trying to turn each element node into values of the following type:

class El(val id: Int, val enabled: Boolean) {
  override def toString = s"El($id, $enabled)"
}

This is done as usual, by declaring an implicit NodeDecoder[El] value. We’ll be using the same decoder method as for case classes, but we don’t have a convenient, pre-existing instance creation function to provide as a parameter and will need to write it ourselves:

import kantan.xpath._
import kantan.xpath.implicits._

implicit val elDecoder: NodeDecoder[El] = NodeDecoder.decoder(xp"./@id", xp"./@enabled") { (id: Int, enabled: Boolean) =>
  new El(id, enabled)
}

Now that we have told kantan.xpath how to decode an XML node to an instance of El, we can simply call evalXPath with the right type parameters:

rawData.evalXPath[List[El]](xp"//element")
// res1: XPathResult[List[El]] = Right(
//   value = List(El(1, true), El(2, false), El(3, true), El(4, false))
// )

Other tutorials: