Search code examples
javaxpathsaxons9api

How to distinguish between attribute and element nodes returned from a Saxon XPathSelector


Given the XML:

<root name="value">
  <level1>
    <level2>Text</level2>
  </level1>
</root>

I want the XPath /root/@name to return value, and the XPath /root/level1 to return the XML serialisation of the <level1> node:

  <level1>
    <level2>Text</level2>
  </level1>

I'm using the a9api interface from Saxon 9.6 in Java.

I've found that I can call XdmValue.toString() to get the XML serialisation of the result of the evaluation of the XPath, which gets me the desired result for selecting an element, but returns name="value" when selecting an attribute. And I can call XdmItem.getStringValue() to get the string value, which gets me the right value for the attribute, but returns the textual content of the element.

Michael Kay has previously said "Saxon's s9api interface ... returns XdmValue objects whose type you can interrogate". I can see that I could perform an instanceof check to determine whether it is an XdmAtomicValue, XdmExternalObject, XdmFunctionItem, or XdmNode, but elements and attributes are both instances of XdmNode. How do I distinguish between the two?

(I can't modify the XPaths, as they're provided by the user.)


Solution

  • I discovered the answer just as I finished writing the question, so I'll share it for others.

    After casting the XdmItem to an XdmNode, you can call XdmNode.getNodeKind(), which returns a value from the XdmNodeKind enumeration specifying which type of node it is:

            XdmValue matchList = xPathSelector.evaluate();
            XdmItem firstItem = matchList.itemAt(0);
            if (firstItem instanceof XdmNode) {
               XdmNode xdmNode = (XdmNode) firstItem;
               XdmNodeKind nodeKind = xdmNode.getNodeKind();
               if (nodeKind == XdmNodeKind.ELEMENT) {
                  return xdmNode.toString();
               }
            }
            return firstItem.getStringValue();