Compiling queries for reuse

In the examples we’ve seen so far, XPath expressions were passed around as XPathExpressions. This can be inefficient as kantan.xpath needs to bake in the decoding code each time they’re evaluated against a node.

When working with expressions that will need to be applied over and over, it’s more efficient to compile them as instances of Query.

In order to show how that works, we’ll 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 extract the id attribute of each element node as an int.

Compiling XPath literals

Compiling XPath literals is done through Query.apply:

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

val query = Query[List[Int]](xp"//element/@id")

You can now use the compiled query where you used to specify strings, such as in evalXPath:

rawData.evalXPath(query)
// res1: ReadResult[List[Int]] = Right(value = List(1, 2, 3, 4))

Note that since compiled queries carry the information of the type they return, you don’t need to specify type parameters to evalXPath.

Compiling strings

You cannot always express your XPath expressions as literals - some expressions are built dynamically, for instance. You can use compile to compile raw strings:

Query.compile[List[Int]]("//element/@id")
// res2: CompileResult[Query[DecodeResult[List[Int]]]] = Right(
//   value = kantan.xpath.Query$$anon$1@5011a5a
// )

The returned value is not directly a Query, though, but rather a CompileResult containing an instance of Query: there is no way to guarantee at compile time that the specified XPath expression is valid, and we must have some error handling in place.


Other tutorials: