menteith
menteith

Reputation: 678

Converting an XML file to CSV file using XSLT (using nested loops)

I'd like to write an XSLT that will transform an XML document to a CSV file. Here's a sample of the XML:

<?xml version="1.0" encoding="utf-8"?>
    <catalog>
        <cd id="c1">
            <singer id="s1">
                <name>Kate</name>
                <surname>Apple</surname>
            </singer>
        <title>Great CD</title>
        </cd>
        <cd id="c2">
            <singer id="s2">
                <name>Mary</name>
                <surname>Orange</surname>
            </singer>
        <title>Even better CD</title>
        </cd>
    </catalog>

The resulting CSV file should be as follows:

singer, title
Kate Apple, Great CD
Mary Orange, Even better CD

I've come up with the following XSLT:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
singer,title
<xsl:for-each select="catalog/cd/singer">
<xsl:value-of select="concat(name,' ',surname,'&#xA;')" />
</xsl:for-each>
<xsl:for-each select="catalog/cd">
<xsl:value-of select="title"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

The resulting output is close to what I'd like to achieve:

singer,title
Kate Apple
Mary Orange
Great CDEven better CD

but the order of elements in incorrect. How do I fix this?

Upvotes: 0

Views: 411

Answers (3)

wargre
wargre

Reputation: 4753

You loop on singer then loop on CD to get title. You need to loop on CD, then get singer and title in that loop.

something like:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
singer,title
<xsl:for-each select="catalog/cd">
    <xsl:value-of select="concat(singer[1]/name,' ',singer[1]/surname,',',title,'&#xA;')" />
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

if you have a namespace in your xml like :

<?xml version="1.0" encoding="utf-8"?>
<library xmlns="http://example.net/library/1.0">
    <cd id="c1">...

then you have to use namespace on XSLT also:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:l="http://example.net/library/1.0">
<xsl:template match="/">
singer,title
    <xsl:for-each select="l:library/l:cd">
        <xsl:value-of select="concat(l:singer[1]/l:name,' ',l:singer[1]/l:surname,',',l:title,'&#xA;')" />
    </xsl:for-each>
</xsl:template>
</xsl:stylesheet>

Upvotes: 0

michael.hor257k
michael.hor257k

Reputation: 116982

If each cd has one singer, then why don't you do simply:

<xsl:template match="/catalog">
    <xsl:text>singer,title&#xA;</xsl:text>
    <xsl:for-each select="cd">
        <xsl:value-of select="concat(singer/name, ' ', singer/surname, ', ', title, '&#xA;')" />
    </xsl:for-each>
</xsl:template>

Upvotes: 2

zx485
zx485

Reputation: 29022

One solution close to a usual CSV is:

<?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:template match="/">
        singer,title
        <xsl:for-each select="catalog/cd">
            <xsl:value-of select="concat(singer/name,' ',singer/surname,',')" />
            <xsl:value-of select="concat(title,'&#xa;')"/>
        </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>

The output is:

singer,title
Kate Apple,Great CD
Mary Orange,Even better CD

Upvotes: 0

Related Questions