0
votes

Sample 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>
</catalog>

Sample XSL:

<table summary="CD Summary" border="1">

            <tr>

                <td>
                    <xsl:for-each select="//cd[1]/node()">
                    <th><xsl:value-of select="name()"/></th>
                    </xsl:for-each>
                </td>

            </tr>
            <xsl:for-each select="//cd">

            <tr>
                <xsl:for-each select="//cd/node()">
                <td>

                    <xsl:value-of select="."/>

                </td>
                </xsl:for-each>

            </tr>
            </xsl:for-each>
        </table>

Output I get:

  • Cell A1 nothing
  • A2-A7 the child node names (CDName, Artist, Country, Company, Price, Year)
  • Cell B1 CD Name (of 1st cd)
  • Cell B2 Artist (of 1st cd)
  • Cell B3 Country (of 1st cd)
  • Cell B4 Company (of 1st cd)
  • Cell B5 Price (of 1st cd)
  • Cell B6 Year (of 1st cd)
  • Cell B7 CD Name (of 2nd cd) and then it repeats above for all cds across the B row.

I want a table which has the headers of the child node names (done but the 1st child starts a new column before the child node first column), and then each cd listed in it's own row under the correct headers. In the real world I will not know how many children each 'cd' node will have until the page is loaded.

Seems like it should be pretty simple and I've tried several variations and I know it's somewhere in the for-each and value-of being in the correct places.

Help, please.

1
Would it be possible for you to provide a slightly bigger sample of your input XML (2 or 3 cds, perhaps), as well as showing the HTML output you would expect from the sample? Thank you!Tim C

1 Answers

1
votes

I noticed a few things that are preventing you from getting the desired output:

  • node() selects more than just elements; try using * instead.
  • Using //cd is selecting every cd element in the document. It's not needed if you pay attention to context. (Hint: Your context is changing in xsl:for-each.)
  • You have an extra td in the first tr.

Try something like this:

<xsl:template match="/*">
    <table summary="CD Summary" border="1">
        <tr>
            <xsl:for-each select="cd[1]/*">
                <th>
                    <xsl:value-of select="name()"/>
                </th>
            </xsl:for-each>
        </tr>
        <xsl:for-each select="cd">
            <tr>
                <xsl:for-each select="*">
                    <td>
                        <xsl:value-of select="."/>
                    </td>
                </xsl:for-each>
            </tr>
        </xsl:for-each>
    </table>
</xsl:template>

Here's another option using a "push" approach. It's done in XSLT 2.0 since you didn't specify a version...

XSLT 2.0

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="/*">
        <table summary="CD Summary" border="1">
            <xsl:apply-templates select="cd[1]" mode="header"/>
            <xsl:apply-templates select="cd"/>
        </table>
    </xsl:template>

    <xsl:template match="cd" mode="#all">
        <tr><xsl:apply-templates mode="#current"/></tr>
    </xsl:template>

    <xsl:template match="*" mode="header">
        <th><xsl:value-of select="name()"/></th>        
    </xsl:template>

    <xsl:template match="*">
        <td><xsl:value-of select="."/></td>
    </xsl:template>

</xsl:stylesheet>