Concept XML XLST preceding-sibling and ancestor

78,133

Solution 1

The preceding-sibling:: axis

The preceding-sibling:: axis is an axis of navigation that includes all the preceding sibling elements to the focus element. By "sibling" we mean a different element which has the same parent to the reference item. By "preceding" we mean a node that occurs before the reference one. The order of the preceding-sibling axis is the reverse document order. Take a look at this document:

<fruit>
  <banana>
   <lady-finger-banana/>
  </banana> 
  <apple/>
  <pear/>
  <kiwi/>
</fruit>

If the focus node is pear, then the sequence preceding-sibling::* is ...

  1. apple
  2. banana

Note: fruit, pear, lady-finger-banana and kiwi are not in the sequence.

So the following is true:

  • preceding-sibling::*[ 1] is the apple
  • preceding-sibling::*[ 2] is the banana
  • count( preceding-sibling::*) is 2
  • preceding-sibling::apple[ 1] is also the apple
  • preceding-sibling::banana[ 1] is the banana
  • preceding-sibling::*[ 3] is absent or the empty sequence

preceding-sibling::pop/ancestor::inventory/totalprice Example

We have to alter your sample document a little bit to usefully study this example

<product>
    <inventory>
        <drink>
            <lemonade>
                <price>$2.50</price>
                <amount>20</amount>
            </lemonade>
            <pop>
                <price>$1.50</price>
                <amount>10</amount>
            </pop>
            <focus-item />
         </drink>
        <totalprice>$15</totalprice>  
    </inventory>
</product>

Let us say the focus is on the element focus-item. To evaluate the expression preceding-sibling::pop/ancestor::inventory/totalprice follow these steps:

  1. preceding-sibling::pop selects all the preceding pop elements to focus-item. This evaluates to a sequence of one node.
  2. For each item in the left hand sequence (just one pop element it so happens), set this item as a temporary focus item, and evaluate the expression of the right of the / operator which is ...

    ancestor::inventory
    

    There is only one such node, which is the ancestral inventory node. Thus the first / operator evaluates to a sequence of one inventory node.

  3. Now we evaluate the effect of the second / and its right-hand operand expression total price. For each item in the left hand sequence (just one inventory node so it happens), set this as a temporary focus item and evaluate totalprice.

  4. totalprice is short for child::totalprice. There is only one total price element on the child axis of the temporary focus node, so the final result is a sequence of one node, which is the total price node.

Understanding by Diagrams

Here is a diagram for preceding-sibling::. In it the reference node is Charlie and the node on the preceding-sibling:: axis is in green. It is the only such node.

enter image description here

Solution 2

Axes useful for navigation through the node tree. So it depends from your problem what kind of axis is useful.

The following stylesheet illustrates the difference.

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"  version="1.0">
   <xsl:output method="xml" indent="yes"/>

   <xsl:template match="snack">
        <xsl:variable name="siblings" select="ancestor::node()"/>
        <debug>
        <xsl:for-each select="preceding-sibling::node()">
                <sibling>
                        <xsl:value-of select="local-name()"/>
                </sibling>
        </xsl:for-each>
        <xsl:for-each select="ancestor::node()">
                <ancestor>
                        <xsl:value-of select="local-name()"/>
                </ancestor>
        </xsl:for-each>

        </debug>
   </xsl:template>

   <xsl:template match="*">
         <xsl:apply-templates select="*"/>
   </xsl:template>
</xsl:stylesheet>

Solution 3

Preceding-sibling gets all element siblings that preceded it in the current node level. Unless you specify one or more of those preceding siblings with an xpath expression. If you specify a specific preceding-sibling with xpath it always starts with 1 in square brackets.

Ancestor is the first matching ancestor that matches the expression. So it goes back up the node tree to look at a matching expression based on where you currently are pointing. So if you were at product/inventory/drink/pop or just /pop then ancestor inventory/totalprice just looks for the frist occurence and it should only return back a pointer to point to that matching case else it will be pointing to nothing and you'll still be pointing at pop.

Share:
78,133
olo
Author by

olo

Updated on April 11, 2020

Comments

  • olo
    olo about 4 years

    I am very new to xslt, and found it can be easy or complex. I want to make clear some concepts. What is preceding-sibling and what is ancestor, after searching from google, I found ancestor explanation. and the chart from their website makes easier to understand.

    But I still don't understand preceding-sibling

    <product>
        <inventory>
            <drink>
                <lemonade>
                    <price>$2.50</price>
                    <amount>20</amount>
                </lemonade>
                <pop>
                    <price>$1.50</price>
                    <amount>10</amount>
                </pop>
            </drink>
              <service>
               <address />
                <phone />
                <delivery> City </delivery>
              </service>      
            <snack>
                <chips>
                    <price>$4.50</price>
                    <amount>60</amount>
                </chips>
            </snack>
            <hotfood></hotfood>
             <totalprice> $15</totleprice>
    
        </inventory>
    </product>
    

    so how do I read this preceding-sibling::pop/ancestor::inventory/totalprice

    ancestor::inventory/totalprice = product\inventory\totalprice preceding-sibling::pop - I dont understand this one then how to read all together?

    Many thanks