简体   繁体   中英

Multiple child nodes of the same name in XSLT

I have the following XML:

<catalog>
    <cd>
        <title>Empire Burlesque</title>
        <artist>Artist 1</artist>
        <country>USA</country>
        <artist>Artist 2</artist>
        <artist>Artist 3</artist>
        <company>Columbia</company>
        <price>10.90</price>
        <year>1985</year>
    </cd>
</catalog>

Now in XSLT I want to loop through the child nodes of <cd> and check if it is a <title> or <artist> or <country> etc... So far I have done the following XSLT:

<xsl:for-each select="catalog/cd">
        <table>
            <tr>
                <th colspan="2"><xsl:value-of select="title"/></th>
            </tr>
            <xsl:choose>
                <xsl:when test="artist">
                    <xsl:apply-templates select="artist"/>
                </xsl:when>
                <xsl:when test="country">
                    <xsl:apply-templates select="country"/>
                </xsl:when>
                <xsl:when test="company">
                    <xsl:apply-templates select="company"/>
                </xsl:when>
                <xsl:when test="price">
                    <xsl:apply-templates select="price"/>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:apply-templates select="year"/>
                </xsl:otherwise>
            </xsl:choose>
</xsl:for-each>

But for some reason <artist> is displaying the first one only the others are not displaying. What I want is to display each node in sequence even if there is <artist> , <country> and another <artist> . Does anyone have any ideas?

You can do this by simply removing the choose altogether. An xsl:choose stops as soon as it hits the first successful when test, essentially you're saying "if there's an artist then show that, else if there's a country then show that, else ...".

<xsl:for-each select="catalog/cd">
    <table>
        <tr>
            <th colspan="2"><xsl:value-of select="title"/></th>
        </tr>
        <xsl:apply-templates select="artist"/>
        <xsl:apply-templates select="country"/>
        <xsl:apply-templates select="company"/>
        <xsl:apply-templates select="price"/>
        <xsl:apply-templates select="year"/>
    </table>
</xsl:for-each>

You don't need to check whether or not elements exist before you apply templates; apply-templates will process all the nodes that its select expression finds, if the select finds nothing then apply-templates will simply do nothing.

If you want to process the elements in document order rather than first the artists, then the countries, etc. then just group them into one apply-templates :

<xsl:apply-templates select="artist | country | company | price | year" />

or if you don't want to have to name all the elements explicitly then move the title logic into its own template

<xsl:template match="title">
  <tr>
    <th colspan="2"><xsl:value-of select="."/></th>
  </tr>
</xsl:template>

and then your main template can simply be

<xsl:for-each select="catalog/cd">
    <table>
        <xsl:apply-templates select="*" />
    </table>
</xsl:for-each>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM