1
votes

How to extract the text of such an element via XPath:

<document>
  some text
     <subelement>subelement text</subelement>
  postscript
</document>

The XPath expression:

/document

returns document node text and all its subnodes text:

some text         subelement text    postscript

While the XPath expression:

/document/text()

returns just the first text node:

some text

that is, "postscript" is missing.

Question
Is there a way to get the text of all text nodes that are immediate sons of <document>?

Postscript
Very focused Example, in case you want to test yourself, copy into a main method and fix the imports.

    DocumentBuilder dbuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();

    String xml = "<?xml version='1.0' encoding='UTF-8'?>" +
                 "<document>"
                 + "some text into document"
                 + "    <subelement>"
                 + "        some text into SUBelement"
                 + "    </subelement>"
                 + "POSTSCRIPT"
                 + "</document>";

    //i'm forced to use an InputSource because parse doesn't take readers directly :-(
    Document doc = dbuilder.parse(new InputSource(new StringReader(xml)));

    //usual way to get an xpath
    XPath xp = XPathFactory.newInstance().newXPath();

    System.out.println(xp.evaluate("/document", doc));

    System.out.println(xp.evaluate("/document/text()",doc));
4

4 Answers

2
votes

Just tested

xp.evaluate("/document/text()",doc, XPathConstants.NODESET)

indeed returns all text children but you are executing

xp.evaluate("/document/text()", doc, XPathConstants.STRING)

which seems to only convert the first node in the node set to String. So maybe you need to find another way to convert the NodeSet to String.

2
votes

This will get you all of the text children. In general, relying on toString() or the methods that try to return String representations will lead to tears when dealing with DOM. It's always safer to "do it fully/do it right."

        NodeList list = (NodeList) xp.evaluate("/document/text()", doc, XPathConstants.NODESET);
        for (int i = 0; i < list.getLength(); i++) {
            System.out.println(list.item(i).getNodeValue());
        }
1
votes

While the XPath expression:

/document/text()

returns just the first text node:

some text into document

that is, "postscript" is missing.

The XPath expression above returns all text node children of /document, but the XPath.evaluate() method, with no 3rd argument converts its result to a string. In the process, it apparently acts like <xsl:value-of> in that it only converts the first node in the result node-set.

To print the value of all text node children, supply XPathConstants.NODESET as the 3rd argument to XPath.evaluate(). This will give you the nodeset of text nodes as a NodeList. Then you can loop through them and print each one. Or you could try passing the NodeList directly to println(), and see what it prints. :-)

1
votes

XPath /document/text() will return all child text nodes of document element. In your example: some text and postscript. I think (I don't know Java classes) System.out.println automatically converts node-set to string representation, in this case it simply returns 1st node.