G.L.
G.L.

Reputation: 9

XSLT 1.0 - Multiple Child Nodes Formatted Output

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

Answers (2)

michael.hor257k
michael.hor257k

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&#10;</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>&#10;</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

har07
har07

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&#xa;</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>&#xa;</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

Related Questions