Question about XPath with functions in it

2019-02-11 02:19发布

this is a follow-up question of my previous thread:

Please help me on understanding this XPath

I have an XPath as:

<xsl:value-of select="position()+count(preceding-sibling::*)-18"/>

currently I can only understand the parts of it, like position(). Also, I know that preceding-sibling is to choose all siblings before the current node, but I have no idea what the statement mean when they get combined like above.

Could anyone give some help on understanding this XPath? thanks in advance.

标签: xml xslt xpath
6条回答
干净又极端
2楼-- · 2019-02-11 02:24

preceding-sibling:: is an axis which returns a nodeset. In this case, * tells it all preceding siblings. count() counts the number of nodes in a nodeset. So, this part of the expression gives us the total number of nodes that have the same parent as the current node which appear before it in the document.

查看更多
对你真心纯属浪费
3楼-- · 2019-02-11 02:28

All answers with the exception of @Alejandro 's have the same common fault:

It is not true that:

preceding-sibling::*

selects all preceding-sibling nodes.

It only selects all preceding-sibling elements.

To select all preceding-sibling nodes use:

preceding-sibling::node()

There are these kind of nodes in XPath:

  1. The root node (denoted as /), also denoted as document-node() in XPath 2.0

  2. Element nodes. such as <a/>

  3. Text nodes. In <a> Hello </a> the text node is the only child of a and has a string value of " Hello "

  4. Comment nodes. <!-- This is a comment-->

  5. Processing instruction nodes. <?someName I am a PI ?>

  6. Attribute nodes. In <a x="1"/> x is the only attribute of a.

  7. Namespace nodes. <a xmlns:my="my:namespace"/> a has a namespace node with value "my:namespace" and name (prefix) my

Nodes of the first 5 kinds can be selected using the preceding-sibling:: axis:

preceding-sibling::node()

selects all sibling nodes of kind 1 to 5.

preceding-sibling::*

selects all element preceding siblings

preceding-sibling::someName

selects all elemens named "someName" preceding siblings

preceding-sibling::text()

selects all text nodes preceding siblings (useful in mixed content)

preceding-sibling::comment()

selects all comment node preceding siblings.

preceding-sibling::processing-instruction()

selects all preceding siblings that are PIs

preceding-sibling::processing-instruction('someName')

selects all preceding siblings that are PIs and are named "someName".

查看更多
叛逆
4楼-- · 2019-02-11 02:29

Your expresion is doing some calculation using the static position (from input source) and the dynamic position (from current node list).

Lets see some examples. Suppose this stylesheet and this input:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="list">
        <xsl:copy>
            <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="*">
        <xsl:copy>
            <xsl:value-of select="concat(position(),' + ',
                                         count(preceding-sibling::*),' = ',
                                         position() +
                                         count(preceding-sibling::*))"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

<list>
    <a/>
    <b/>
    <a/>
    <b/>
</list>

Output:

<list>
    <a>1 + 0 = 1</a>
    <b>2 + 1 = 3</b>
    <a>3 + 2 = 5</a>
    <b>4 + 3 = 7</b>
</list>

Now, changing the second rule to match="a":

<list>
    <a>1 + 0 = 1</a>
    <a>3 + 2 = 5</a>
</list>

So, patterns don't change the current node list

What if position() is in pattern? Lets change the rule to match="a[position()=2]":

<list>
    <a>3 + 2 = 5</a>
</list>

Strange? No. In the XPath pattern position() works against its own context node list and the axis direction. This case: child::a[position()=2] meaning a second a child.

This shows that position() in patterns works with different context than position() in the content template.

So, How change the current context node list? Well, apply-templates and for-each instructions.

Add now to the apply-templates instruction some select attribute like select="a":

<list>
    <a>2 + 2 = 4</a>
</list>
查看更多
成全新的幸福
5楼-- · 2019-02-11 02:31

Your code will result in a number, which will equal:

The position in the Current node list, minus the number of any preceding siblings, minus 18.

Generally speaking, different operators can be combined into expressions as you demonstrate in your example.

A note: Use position() with caution, because sometimes the current node list is not easy to see.

查看更多
Melony?
6楼-- · 2019-02-11 02:49

position() returns the position of the current node within the node-set being iterated right now. Assume there are four <foo> elements:

<xml>
  <foo /><foo /><foo /><foo />
</xml>

and you iterate them via <xsl:apply-templates>:

<xsl:template match="/xml">
  <!-- this selects four nodes -->
  <xsl:apply-templates select="foo" />
</xsl:template>

<!-- this runs four times -->
<xsl:template match="foo">
  <xsl:value-of select="position()" />
</xsl:template>

Then this will output "1234".

count() counts the nodes in a node-set.

preceding-sibling::* selects all elements on the preceding-sibling axis, as seen from the current node (unless the current node is an attribute, as attributes technically do not have preceding siblings).

<xsl:value-of select="position()+count(preceding-sibling::*)-18"/>

Should be pretty self-explanatory now. The XSLT concept you are probably missing is that of the "current node". The current node is the execution context of your XSLT program. There is always a node that is the current node, and most XSLT/XPath operations implicitly work on the current node.

查看更多
Juvenile、少年°
7楼-- · 2019-02-11 02:49

position() will evaluate to a number representing the current nodes position within a 1 indexed array of nodes being evaluated. Call that n.

preceding-sibling::* will evaluate the number of sibling nodes before the current one and count is what it sounds like (and quite likely to be equal to n-1 here as it goes).

The -18 should be self-evident :) so what you're left with is a calculation of the position + the number of preceding siblings - 18. That's going to be quite specific to the business concern as to why you want that calculation, but that's what you have.

查看更多
登录 后发表回答