What can be parsed as XML data?

Before we can even think about applying XPath expressions to an XML document, we need to get our hands on that document somehow. kantan.xpath extends most things that “can be turned into XML” with useful methods, such as the oft-used evalXPath method. Among such things are:

This is done through the XmlSource type class: any type A such that there exists a value of type XmlSource[A] in the implicit scope will be enriched with useful methods.

Implementing our own XmlSource for types that aren’t supported by default is fairly simple.

Implementation from scratch

Reduced to its simplest expression, an XmlSource[A] is essentially an A => ParseResult[Node] - that is, a function that takes an A and turns it into a Node, with the possibility of safe failure encoded in ParseResult.

If you can write such a function, you can trivially turn it into a valid instance of XmlSource. A simple example would be to provide an XmlSource instance for Node:

import kantan.xpath._

implicit val node: XmlSource[Node] = XmlSource.from(ParseResult.success)

Adapting existing instances

A more idiomatic way of writing new XmlSource instances, however, is to adapt existing ones through contramap or contramapResult. The most useful instance for such purpose is the one that exists for InputSource, provided an implicit instance of XmlParser is in scope.

XmlParser is a concept most people will never really need to concern themselves about - a reasonable default implementation is always in scope. The only reason to write a new one would be to change XML parsing behaviour, as the nekohtml module does.

In order to write an XmlSource instance for String, say, one would only need to know how to turn an instance of String into one of InputSource:

implicit def stringSource(implicit parser: XmlParser): XmlSource[String] =
  XmlSource[InputSource].contramap(s => new InputSource(new java.io.StringReader(s)))

Other tutorials: