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