XPath Selection - scalawag/sdom GitHub Wiki
You can perform XPath 1.0 selection against a Document using the SDOM's XPath operators.
- %% - select Nodes using XPath => Selection[Node]
- %< - select Elements using an XPath expression => Selection[Element]
- %@ - select Attributes using an XPath expression => Selection[Attribute]
- % - select an arbitrary value using an XPath expression => Selection[Any]
Since XPath is not type-safe (there's no way to statically look at the expression to determine what it will return), all of these operators are really specializations of the last one. The earlier ones simply check the results after the expression has been executed and cast the result to the appropriate type. This makes it so that your code doesn't have to do all the casting and (hopefully) remains more readable.
import org.scalawag.sdom._
val x1 = XML.parse("""<a xmlns="A"><b xmlns="B" id="1"/><b xmlns="C" id="2"><c>8</c></b><d><b id="3"/></d></a>""")
x1 % "count(//b) > 2"
// -> Selection[Any] = Selection(false)
x1 % "count(//*) > 2"
// -> Selection[Any] = Selection(true)
x1 %@ "//*/@id[. >= 2]"
// -> Selection[Attribute] = Selection(Attribute(id="2"), Attribute(id="3"))
x1 %< "//*/@id[. >= 2]/.."
// -> Selection[Element] = Selection(Element(<b id="2" xmlns="C"><c>8</c></b>), Element(<b id="3"/>))
x1 %% "//*/@id[. >= 2]"
// -> Selection[Node] = Selection(Attribute(id="2"), Attribute(id="3"))
The last two select the same set of Nodes. The only difference is the type of the return value. With the narrower types, you can continue using chained selectors. It's fine to mix XPath selection with regular selection.
x1 \\ * %< "./@id[. < 2]/.." %@ "@id"
// -> Selection[Attribute] = Selection(Attribute(id="1"))
This particular selection is incredibly inefficient. It's just here to show the type of thing you can do. The first operator selects all Elements in the Document. The second operator selects the parent Elements of all "id" Attributes whose id is less than 2. The last operator selects the "id" Attributes of those Elements.
XPath selections uses the implicit Namespaces instance for prefix to namespace mapping just like normal selection. It does not, however, use the default namespace for unprefixed local names. This is according to the XPath specification.
x1 %< "//b"
// -> Selection[Element] = Selection()
{
implicit val namespaces = Namespaces("n" -> "B")
x1 %< "//n:b"
}
// -> Selection[Element] = Selection(Element(<b id="1" xmlns="B"/>))
{
implicit val namespaces = Namespaces("" -> "B")
x1 %< "//b"
}
// -> Selection[Element] = Selection()