Reputation: 64
I have an XML structure like the following:
<inventory>
<location>
<category>
<children>
<book>
<title>Harry Potter</title>
<price>$28.50</price>
</book>
<cd>
<title>Frozen</title>
<price>12.8</price>
</cd>
</children>
</category>
<category>
<adult>
<book>
<title>Da vinci code</title>
<price>32.50</price>
</book>
<cd>
<title>Da vinci code</title>
<price>13.80</price>
</cd>
</adult>
</category>
</location>
<location>
<category>
<cooking>
<book>
<title>everyday Italian</title>
<price>30.50</price>
</book>
</cooking>
</category>
</location>
</inventory>
What I want to print is:
Location category# category title price 1 1 children Harry 28.50 2 1 cooking everyday... 30.50 1 2 cd Da vinci code 13.8
If I'm currently on each of the <title>
elements, how do I get the position of <location>
and <category>
?
What I've tried:
count(ancestor::location/preceding-silbing::location) + 1
count(ancestor::category/preceding-silbing::category) + 1
But none of them work.
Upvotes: 0
Views: 2405
Reputation: 52848
Since you're using XSLT 2.0 try using the more powerful xsl:number
instead of count()
.
Location:
<xsl:number select="ancestor::location"/>
Category:
<xsl:number select="ancestor::category"/>
Here's a more detailed example. (Note, this is just for illustration purposes; it's probably not the most efficient example.)
XML Input
<inventory>
<location>
<category>
<children>
<book>
<title>Harry Potter</title>
<price>$28.50</price>
</book>
<cd>
<title>Frozen</title>
<price>12.8</price>
</cd>
</children>
</category>
<category>
<adult>
<book>
<title>Da vinci code</title>
<price>32.50</price>
</book>
<cd>
<title>Da vinci code</title>
<price>13.80</price>
</cd>
</adult>
</category>
</location>
<location>
<category>
<cooking>
<book>
<title>everyday Italian</title>
<price>30.50</price>
</book>
</cooking>
</category>
</location>
</inventory>
XSLT 2.0
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:local="local">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="column-widths">
<cols>
<col name="location" width="10"/>
<col name="categorynbr" width="11"/>
<col name="category" width="{max((string-length('category'),//category/*/string-length(name())))+2}"/>
<col name="title" width="{max((string-length('title'),//category/*/book/title/string-length(normalize-space())))+2}"/>
<col name="price" width="{max((string-length('price'),//category/*/book/price/string-length(normalize-space())))+2}"/>
</cols>
</xsl:variable>
<xsl:function name="local:padValue">
<xsl:param name="colname"/>
<xsl:param name="value"/>
<xsl:variable name="padding">
<xsl:for-each select="1 to xs:integer($column-widths/*/col[@name=$colname]/@width) - string-length($value)">
<xsl:text> </xsl:text>
</xsl:for-each>
</xsl:variable>
<xsl:value-of select="concat($value,$padding)"/>
</xsl:function>
<xsl:template match="/*">
<xsl:value-of select="concat(local:padValue('location','Location'),
local:padValue('categorynbr','category#'),
local:padValue('category','category'),
local:padValue('title','title'),
local:padValue('price','price'),'
')"/>
<xsl:apply-templates select="*/*/*/book">
<xsl:sort>
<xsl:number select="ancestor::category"/>
</xsl:sort>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="book">
<xsl:variable name="loc">
<xsl:number select="ancestor::location"/>
</xsl:variable>
<xsl:variable name="cat">
<xsl:number select="ancestor::category"/>
</xsl:variable>
<xsl:value-of select="concat(local:padValue('location',$loc),
local:padValue('categorynbr',$cat),
local:padValue('category',../local-name()),
local:padValue('title',normalize-space(title)),
local:padValue('price',normalize-space(price)),'
')"/>
</xsl:template>
</xsl:stylesheet>
Output
Location category# category title price
1 1 children Harry Potter $28.50
2 1 cooking everyday Italian 30.50
1 2 adult Da vinci code 32.50
Upvotes: 1
Reputation: 464
My assumption is that this is what you're after:
Your context node is either of the title
elements in the input document. Now you want to know
The (one-based) index of the location
element that your context node is contained in, within its parent inventory
element
The (one-based) index of the category
element that your context node is contained in, within its parent category
element
For the provided XML input, this can be achieved with the XPath expressions
count(ancestor::location/preceding-sibling::location) + 1
and
count(ancestor::category/preceding-sibling::category) + 1
Make sure that axis and node-test are separated by two colons (corrected by edit). Also make sure preceding-sibling
is spelled correctly.
You can add the predicate [1]
after the location steps ancestor::location
and ancestor::category
to explicitly state that you're after the first ancestor in reverse document order (i.e., the closest ancestor). This would be necessary if the XML contained nested location
or category
elements.
Upvotes: 0