Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Find position of parent node using xpath

How to get the position of parent node in the complete document using xpath?

say I have the following xml:

<catalog>
  <cd>
    <title>Empire Burlesque</title>
    <artist>Bob Dylan</artist>
    <country>USA</country>
    <company>Columbia</company>
    <price>10.90</price>
    <year>1985</year>
  </cd>
  <cd>
    <title>Hide your heart</title>
    <artist>Bonnie Tyler</artist>
    <country>UK</country>
    <company>CBS Records</company>
    <price>9.90</price>
    <year>1988</year>
  </cd>
</catalog>

and I have a XSLT to convert it into HTML, which is as follows (only snippet):

<xsl:template match="/">
<html>
  <body>  
  <xsl:apply-templates/>  
  </body>
  </html>
</xsl:template>

<xsl:template match="cd">
  <p>
    <xsl:number format="1. "/><br/>
    <xsl:apply-templates select="title"/>  
    <xsl:apply-templates select="artist"/>
  </p>
</xsl:template>

<xsl:template match="title">
  <xsl:number format="1" select="????" /><br/>
  Title: <span style="color:#ff0000">
  <xsl:value-of select="."/></span>
  <br />
</xsl:template>

What should I write at the place of ???? to get position of the parent cd tag in the document. I have tried many expressions but nothing seems to be working. May be I am doing it altogether wrong.

  1. <xsl:number format="1" select="catalog/cd/preceding-sibling::..[position()]" />
  2. <xsl:number format="1" select="./parent::..[position()]" /><br/>
  3. <xsl:value-of select="count(cd/preceding-sibling::*)+1" /><br/>

I am interpreting 2nd as select current node's parent axis and then tell the position of parent of the current node. Why is it not working? What is the correct way to do this.

FYI: I expect the code to print the position of parent cd tag of the current title tag uder processing.

Please can someone tell me how to do this.

like image 257
Harshdeep Avatar asked Jun 30 '12 09:06

Harshdeep


2 Answers

count(../preceding-sibling::cd) + 1

You can run it here (note I removed the other number you were outputting, just for clarity).

You were on the right lines, but remember that predicates are used only to filter nodes, not to return information. So:

../*[position()]

...effectively says "find me the parent that has a position". It returns the node, not the position itself. The predicate is just a filter.

In any case there are pitfalls with using position(), and it can be used to return the position of the current, context node only - not another node.

like image 122
Mitya Avatar answered Oct 06 '22 09:10

Mitya


Utkanos's answer works fine but my experience is that, when the xml document is large, this could lead to performance issues.

In this scenario you could simply pass the position of the parent in a param.

<xsl:template match="/">
<html>
  <body>  
  <xsl:apply-templates/>  
  </body>
  </html>
</xsl:template>

<xsl:template match="cd">
  <p>
    <xsl:number format="1. "/><br/>
    <xsl:apply-templates select="title">  
        <xsl:with-param name="parent_position" select="position()"/> <!-- Send here -->
    </xsl:apply-templates>
    <xsl:apply-templates select="artist"/>
  </p>
</xsl:template>

<xsl:template match="title">
  <xsl:param name="parent_position"/> <!-- Receive here -->
  <xsl:number format="1" select="$parent_position"/><br/>
  Title: <span style="color:#ff0000">
  <xsl:value-of select="."/></span>
  <br />
</xsl:template>

result:

<html xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><body>
  <p>1. <br>1<br>
  Title: <span style="color:#ff0000">Empire Burlesque</span><br>Bob Dylan</p>
  <p>2. <br>1<br>
  Title: <span style="color:#ff0000">Hide your heart</span><br>Bonnie Tyler</p>
</body></html>
like image 20
thehpi Avatar answered Oct 06 '22 11:10

thehpi