XPath - Difference between node() and text()
Asked Answered
L

2

70

I'm having trouble understanding the difference between text() and node(). From what I understand, text() would be whatever is in between the tags <item>apple</item> which is apple in this case. Node would be whatever that node actually is, which would be item

But then I've been assigned some work where it asks me to "Select the text of all items under produce" and a separate question asks "Select all the manager nodes in all departments"

How is the output suppose to look text() as opposed to node()

Snippet of XML:

<produce>
 <item>apple</item>
 <item>banana</item>
 <item>pepper</item>
</produce>

<department>
 <phone>123-456-7891</phone>
 <manager>John</manager>
</department>

Of course, there are more departments and more managers, but this was just a snippet of code.

Any help would be much appreciated!

Laxity answered 31/7, 2012 at 16:9 Comment(0)
C
139

text() and node() are node tests, in XPath terminology (compare).

Node tests operate on a set (on an axis, to be exact) of nodes and return the ones that are of a certain type. When no axis is mentioned, the child axis is assumed by default.

There are all kinds of node tests:

  • node() matches any node (the least specific node test of them all)
  • text() matches text nodes only
  • comment() matches comment nodes
  • * matches any element node
  • foo matches any element node named "foo"
  • processing-instruction() matches PI nodes (they look like <?name value?>).
  • Side note: The * also matches attribute nodes, but only along the attribute axis. @* is a shorthand for attribute::*. Attributes are not part of the child axis, that's why a normal * does not select them.

This XML document:

<produce>
    <item>apple</item>
    <item>banana</item>
    <item>pepper</item>
</produce>

represents the following DOM (simplified):

root node
   element node (name="produce")
      text node (value="\n    ")
      element node (name="item")
         text node (value="apple")
      text node (value="\n    ")
      element node (name="item")
         text node (value="banana")
      text node (value="\n    ")
      element node (name="item")
         text node (value="pepper")
      text node (value="\n")

So with XPath:

  • / selects the root node
  • /produce selects a child element of the root node if it has the name "produce" (This is called the document element; it represents the document itself. Document element and root node are often confused, but they are not the same thing.)
  • /produce/node() selects any type of child node beneath /produce/ (i.e. all 7 children)
  • /produce/text() selects the 4 (!) whitespace-only text nodes
  • /produce/item[1] selects the first child element named "item"
  • /produce/item[1]/text() selects all child text nodes (there's only one - "apple" - in this case)

And so on.

So, your questions

  • "Select the text of all items under produce" /produce/item/text() (3 nodes selected)
  • "Select all the manager nodes in all departments" //department/manager (1 node selected)

Notes

  • The default axis in XPath is the child axis. You can change the axis by prefixing a different axis name. For example: //item/ancestor::produce
  • Element nodes have text values. When you evaluate an element node, its textual contents will be returned. In case of this example, /produce/item[1]/text() and string(/produce/item[1]) will be the same.
  • Also see this answer where I outline the individual parts of an XPath expression graphically.
Corking answered 31/7, 2012 at 16:27 Comment(3)
It's an excellent answer, but for the record, there are a couple of inaccuracies. (a) The meaning of the node-test "*" depends on the axis: with most axes, it selects element nodes, but with the attribute axis it selects attributes, and with the namespace axis it selects namespaces. (b) @* and @foo are not node-tests, but axis steps, consisting of two parts: an axis (@, which is short for attribute::), and a node-test (* or foo).Bunco
@MichaelKay Thank you for the clarification. I'll modify the misleading bits. Axes are outside the scope of this question anyway, but it's hard to avoid mentioning them once you're talking about XPath.Corking
@tomalak How then is comment part defined among those above (that is: <!-- --->) ?Chlamys
M
0

For me it was a big difference when I faced this scenario (here my story:)

<?xml version="1.0" encoding="UTF-8"?>
<sentence id="S1.6">When U937 cells were infected with HIV-1, 
        
    <xcope id="X1.6.3">
        <cue ref="X1.6.3" type="negation">no</cue> 
                        
                        induction of NF-KB factor was detected
        
    </xcope>
                    
, whereas high level of progeny virions was produced, 
        
    <xcope id="X1.6.2">
        <cue ref="X1.6.2" type="speculation">suggesting</cue> that this factor was 
        <xcope id="X1.6.1">
            <cue ref="X1.6.1" type="negation">not</cue> required for viral replication
        </xcope>
    </xcope>.

</sentence>

I needed to extract text between tags and aggregate (by concat) the text including in innner tags.

/node() did the job, while /text() made half job

/text() only returned text not included in inner tags, because inner tags are not "text nodes". You may think, "just extract text included in the inner tags in an additional xpath", however, it becomes challenging to sort the text in this original order because you dont know where to place the aggregated text from the inner tags!because you dont know where to place the aggregated text from the inner nodes.

  1. When U937 cells were infected with HIV-1,
  2. no induction of NF-KB factor was detected
  3. , whereas high level of progeny virions was produced,
  4. suggesting that this factor was not required for viral replication
  5. .

Finally, /node() did exactly what I wanted, because it gets the text from inner tags too.

Mainland answered 11/8, 2020 at 7:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.