2

I have an xml like this one:

<?xml version="1.0" encoding="WINDOWS-1252" ?> <?xml-stylesheet type="text/xsl" href="test.xsl"?> <root> <something> <cd>a1</cd> <cd>a2</cd> </something> <another> <cd>b1</cd> <cd>b2</cd> <cd>b3</cd> <cd>b1</cd> <cd>b2</cd> <cd>b3</cd> <cd>b1</cd> <cd>b2</cd> </another> </root> 

I'm selecting all the cd nodes and trying to take the cds from 10 to 10 (1st, 11th, 21th...) this way:

<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/"> <html><body> <xsl:apply-templates select="//cd" /> </body></html> </xsl:template> <xsl:template match="cd"/> <xsl:template match="cd[position() mod 10 = 1]"> <div> pos:<xsl:value-of select="position()"/>; value:<xsl:value-of select="."/> </div> </xsl:template> </xsl:stylesheet> 

The output:

 pos:1; value:a1 pos:3; value:b3 

And the expected output:

pos:1; value:a1 pos:11; value:b11 

The problem is that position() in the match is the node number based on his parent. So the position() of "b1" is 1 and not 3. Is there a function to know the index of the current node?

Thanks in advance.

//EDIT to extend the example

3
  • 1
    Can you explain what "And I'm trying to take the cds from 10 to 10 this way" means? Are you trying to display the first 10 cds? Commented Mar 12, 2014 at 18:18
  • 1
    You can set a variable containing all CDs <xsl:variable name="all-cds" select="//cd" /> and then loop through them <xsl:for-each select="$all-cds"> and you will get their position independent of their parents. Commented Mar 12, 2014 at 18:20
  • @DanielHaley I want the 1st element, the 11th, the 21th, etc. I've just edited the question to extend the example Commented Mar 12, 2014 at 18:53

3 Answers 3

2

You can get a more accurate count by using xsl:number:

<xsl:template match="cd"> <div>pos: <xsl:number level="any"/></div> </xsl:template> 

You could also count the preceding cd elements:

<xsl:template match="cd"> <div>pos: <xsl:value-of select="count(preceding::cd)+1"/></div> </xsl:template> 

I prefer xsl:number, but I suppose it depends on how you're using the value. If you give a better example of what you're trying to do with that number, I can update my answer.

Example based on question update:

Input

<root> <something> <cd>a1</cd> <cd>a2</cd> </something> <another> <cd>b1</cd> <cd>b2</cd> <cd>b3</cd> <cd>b1</cd> <cd>b2</cd> <cd>b3</cd> <cd>b1</cd> <cd>b2</cd> </another> <another> <cd>c1</cd> <cd>c2</cd> <cd>c3</cd> <cd>c1</cd> <cd>c2</cd> <cd>c3</cd> <cd>c1</cd> <cd>c2</cd> </another> </root> 

XSLT 1.0

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/"> <html><body> <xsl:apply-templates select="//cd" /> </body></html> </xsl:template> <xsl:template match="cd"/> <xsl:template match="cd[(count(preceding::cd)+1) mod 10 = 1]"> <div> pos:<xsl:value-of select="position()"/>; value:<xsl:value-of select="."/> </div> </xsl:template> </xsl:stylesheet> 

Output

<html> <body> <div> pos:1; value:a1 </div> <div> pos:11; value:c1 </div> </body> </html> 
Sign up to request clarification or add additional context in comments.

2 Comments

Yes, but in the template match the position is refering to his parents and I want it to be the index of the selection
@MazarD - Huh? Don't use position() at all. See my edit for an example.
2

The position() in the match of template filters "cd"s based on position in its parent:

<xsl:template match="cd[position() mod (10) = 1]"> 

and the position() inside the template definition,

<div>pos:<xsl:value-of select="position()"/></div> 

gives you the cd's sequence which the template has been applied for.

Thus, "position() mod (10) = 1" filters only those "cd"s which are 1st, 11th, 21st, etc. children of their parents.

Hence, you can remove the filter from xsl:template's match and put it inside like this:

<xsl:template match="cd"> <div>pos:<xsl:if test="position() mod 10 = 1"> <xsl:value-of select="position()"/> </xsl:if> </div> </xsl:template> 

Comments

1

This will save all the CDs in a variable. In the for-each, the context does not depend on the parent nodes so you will get the position among all CDs:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:variable name="all-cds" select="//cd" /> <xsl:template match="root"> <xsl:for-each select="$all-cds"> <div>pos: <xsl:value-of select="position()"/></div> </xsl:for-each> </xsl:template> </xsl:stylesheet> 

1 Comment

I finally used the xsl:if to test the position instead of the template match, but your solution is great, thank you, I will surely use it in the future.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.