Reputation: 9
I can do this with For-Each but having some problems with Apply_Templates.
The XML Data contains varying child nodes but I want to display all child nodes with their parents information.
<orders>
<order>
<customer>1</customer>
<items>
<item>
<name>Widget A</name>
<price>10</price>
<code>1A</code>
</item>
</items>
</order>
<order>
<customer>2</customer>
<items>
<item>
<name>Widget A</name>
<price>10</price>
<code>1A</code>
</item>
</items>
<items>
<item>
<name>Widget B</name>
<price>20</price>
<code>2B</code>
</item>
</items>
</order>
<order>
<customer>3</customer>
<items>
<item>
<name>Widget A</name>
<price>10</price>
<code>1A</code>
</item>
</items>
<items>
<item>
<name>Widget B</name>
<price>10</price>
<code>1A</code>
</item>
</items>
<items>
<item>
<name>Widget C</name>
<price>30</price>
<code>3C</code>
</item>
</items>
</order>
As you can see, there are multiple item per customer per order. I want the output to look like this:
Customer, Name, Price, Code
1, Widget A, 10, 1A
2, Widget A, 10, 1A
2, Widget B, 20, 2B
3, Widget A, 10, 1A
3, Widget B, 20, 2B
3, Widget C, 30, 3C
When I to apply-templates, I can only get the first item to display but not the others. If you think I should post my XSL, let me know but I think it should be simple just stuck. I want to get away from using for-each.
Upvotes: 0
Views: 238
Reputation: 117003
I believe a truly template-based approach would look like this:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="utf-8"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/orders">
<xsl:text>Customer, Name, Price, Code </xsl:text>
<xsl:apply-templates select="order/items/item"/>
</xsl:template>
<xsl:template match="item">
<xsl:value-of select="../../customer"/>
<xsl:text>, </xsl:text>
<xsl:apply-templates/>
<xsl:if test="position()!=last()">
<xsl:text> </xsl:text>
</xsl:if>
</xsl:template>
<xsl:template match="item/*">
<xsl:value-of select="."/>
<xsl:if test="position()!=last()">
<xsl:text>, </xsl:text>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
However, as I mentioned in the comment to your question, this serves no discernible purpose, other than breaking the code into discontinuous chunks that are hard to follow.
Upvotes: 0
Reputation: 89295
You can try this way :
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" indent="no"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:text>Customer, Name, Price ,Code
</xsl:text>
<xsl:apply-templates select="//item"/>
</xsl:template>
<xsl:template match="item">
<xsl:value-of select="../../customer"/>
<xsl:text>, </xsl:text>
<xsl:value-of select="name"/>
<xsl:text>, </xsl:text>
<xsl:value-of select="price"/>
<xsl:text>, </xsl:text>
<xsl:value-of select="code"/>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
First template match the root element and print header in the output document.
The 2nd template match item
elements and output corresponding comma-separated values. This way you can have rows from each item
in the output document without using for-each
construct.
Upvotes: 1