Subject: [netmod] XPath node type tests

From: Robert Wilton <[email-masked]>
Date: Fri, 20 Oct 2017 11:13:33 +0100

Hi,

XPATH 1.0 defines the following three node-type tests:

1) comment()
2) processing-instruction(<opt arg>)
3) text()

My assumption is that a YANG tree doesn't contain any nodes of type 
'comment' or 'processing-instruction' and hence these filters would 
never match any nodes.

However, it wasn't clear to me from reading 7950 or rfc6087bis-14 
whether text() matches anything.  In particular, does a YANG leaf node 
(except of type empty) always parent a text node that holds its value?

Thanks,
Rob

---

From: Ladislav Lhotka <[email-masked]>
Date: Fri, 20 Oct 2017 17:27:27 +0200

Hi Rob,

> 2) processing-instruction(<opt arg>)

For completeness, node() is the fourth one.

> 'comment' or 'processing-instruction' and hence these filters would 

Yes. FWIW, Yangson library raises NotSupported exception upon
encountering these.

> (except of type empty) always parent a text node that holds its value?

I believe this is how it should be interpreted. According to XPath 1.0
spec, comparisons like

    xyz = 'foo'

use string-value of xyz node, which is defined as the concatenation of
the string-values of all text node descendants of xyz.

Lada

>
> Thanks,
> Rob
>
>
>
> _______________________________________________
> netmod mailing list
> [email-masked]
> https://www.ietf.org/mailman/listinfo/netmod

-- 
Ladislav Lhotka
Head, CZ.NIC Labs
PGP Key ID: 0xB8F92B08A9F76C67

---

From: Robert Wilton <[email-masked]>
Date: Fri, 20 Oct 2017 17:24:41 +0100

Hi Lada,

Thanks for the explanation, that makes sense.

> the string-values of all text node descendants of xyz.
Yes.  I don't think that I've ever come across for XPath usage in YANG 
where the "concatenation of the string-values of all text node 
descendants " is actually useful (particularly as the children nodes are 
likely to not be consistently ordered).

Thanks,
Rob

---

From: Andy Bierman <[email-masked]>
Date: Fri, 20 Oct 2017 09:55:27 -0700

>> Yes. FWIW, Yangson library raises NotSupported exception upon

But a server or client should ignore PIs, not reject the XML.

I think text() and node() are just filter tests.

  /foo/*[text()] would return all the child nodes of /foo that are leaf or
leaf-list

text() returns a boolean (0 or 1).  Do not use it for value testing:

  /foo/*[text() = 'fred']  // wrong!

  /foo/*[. = 'fred']  // correct

[7]    NodeTest    ::=    NameTest
<https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-NameTest>
| NodeType <https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-NodeType> '('
')'
| 'processing-instruction' '(' Literal
<https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-Literal> ')'

> where the "concatenation of the string-values of all text node descendants
> " is actually useful (particularly as the children nodes are likely to not
> be consistently ordered).
>
>

I think text() and node() are just filter tests.

  /foo/*[text()] would return all the child nodes of /foo that are leaf or
leaf-list

text() returns a boolean (0 or 1).

[7]    NodeTest    ::=    NameTest
<https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-NameTest>
| NodeType <https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-NodeType> '('
')'
| 'processing-instruction' '(' Literal
<https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-Literal> ')'

[38]    NodeType    ::=    'comment'
| 'text'
| 'processing-instruction'
| 'node'

The node test text() is true for any text node. For example, child::text() will
select the text node children of the context node. Similarly, the node test
comment() is true for any comment node, and the node test
processing-instruction() is true for any processing instruction. The
processing-instruction() test may have an argument that is Literal
<https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-Literal>; in this case,
it is true for any processing instruction that has a name equal to the
value of the Literal
<https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-Literal>.

Thanks,
>> Lada

Andy

---

From: Martin Bjorklund <[email-masked]>
Date: Mon, 23 Oct 2017 11:10:41 +0200 (CEST)

> text() returns a boolean (0 or 1).  Do not use it for value testing:

No.  text() will select the text node children of the context node.

>   /foo/*[text() = 'fred']  // wrong!

This actually works.  text() selects all text nodes (just one for a
leaf), and then that text node is compared to the string 'fred'.

/martin

---

From: Robert Wilton <[email-masked]>
Date: Mon, 23 Oct 2017 11:05:58 +0100

> No.  text() will select the text node children of the context node.
This is presumably because text() is evaluated as "child::text()".

> leaf), and then that text node is compared to the string 'fred'.
For clarity, am I right in my interpretation that a leaf is not itself a 
text node, but instead a leaf is an element node that contains a direct 
child text node?

Presumably, it is only leaf and leaf-list element nodes that can have 
these direct child text nodes.

I can see how this make sense for a XML document, but it does feel a bit 
non intuitive for a YANG data tree, and it may be helpful if this is 
documented somewhat ...

Presumably this test isn't quite the same, since child container and 
list nodes would also be included in the comparison (i.e. by 
concatenating all their descendant leaf values together into a single 
string), whereas the expression with the text() check will only include 
the values of direct child leaf and leaf-list nodes (as YANG is 
currently defined today).

Thanks,
Rob

> .

---

From: Martin Bjorklund <[email-masked]>
Date: Mon, 23 Oct 2017 12:37:34 +0200 (CEST)

> This is presumably because text() is evaluated as "child::text()".

Yes.

> For clarity, am I right in my interpretation that a leaf is not itself
> a text node, but instead a leaf is an element node that contains a
> direct child text node?

Yes.

> these direct child text nodes.

Yes.

> I can see how this make sense for a XML document, but it does feel a
> bit non intuitive for a YANG data tree

Maybe, but since we use XPath, we need to conform to the data model
used by XPath (see section 5 of the xpath spec).

> and it may be helpful if this
> is documented somewhat ...

RFC 7950 refers to the data model of XPath (See section 6.4 of RFC
7950), but I agree that it could have had more text.  Specifically, it
could have stated how nodes are mapped to elements, that only
leaf/leaf-list have text nodes; that annotations are mapped to
attribute nodes (ok, not really in 7950...); that there are no
processing-instruction and comment nodes.

> string)
> whereas the expression with the text() check will only
> include the values of direct child leaf and leaf-list nodes (as YANG
> is currently defined today).

Yes.

/martin

---

From: Ladislav Lhotka <[email-masked]>
Date: Mon, 23 Oct 2017 13:33:12 +0200

> On 23 Oct 2017, at 12:37, Martin Bjorklund <[email-masked]> wrote:
> 

In principle, there could be multiple text nodes (in XML processing this is quite common).

Lada

---

From: Martin Bjorklund <[email-masked]>
Date: Mon, 23 Oct 2017 13:36:50 +0200 (CEST)

Ladislav Lhotka <[email-masked]> wrote:
> In principle, there could be multiple text nodes

Do you mean for a YANG leaf?

/martin

> (in XML processing
> this is quite common).

---

From: Phil Shafer <[email-masked]>
Date: Thu, 26 Oct 2017 12:27:07 -0400

Andy Bierman writes:
>  /foo/*[text() =3D 'fred']  // wrong!
>
>  /foo/*[. =3D 'fred']  // correct

I haven't looked at the spec, but this isn't true for libxml2/libxslt/libslax:

(sdb) n
4: var $foo := <top> { <one> "one"; <two> "two"; }
(sdb) n
2: main <foo> { }
(sdb) p $foo
[node-set] (1) rtf-doc
<top>
  <one>one</one>
  <two>two</two>
</top>

(sdb) p $foo/*
[node-set] (1)
<top>
  <one>one</one>
  <two>two</two>
</top>

Outside predicates, it's a node test, matching text nodes:

(sdb) p $foo/*/text()
[node-set] (0)

(sdb) p $foo/*/*/text()
[node-set] (2)
one
two

But inside predicates is does return the text, probably like string():

(sdb) p $foo/*/*
[node-set] (2)
<one>one</one>
<two>two</two>

(sdb) p $foo/*[text() == 'one']
[node-set] (0)

(sdb) p $foo/*/*[text() == 'one']
[node-set] (1)
<one>one</one>

It could be just a bug in libxml2;  I'll take a look.

Thanks,
 Phil

P.s.: the command line I used was:

   slaxproc -d -m 'main <foo> { }' -E -m 'var $foo := <top> { <one> "one"; <two> "two"; }'

The ":=" operator turns RTFs into node-sets (via calling node-set()).