0

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?

2
  • Can one assume that each cd has exactly one singer? If not, what should the result when there are two or none? Commented Jul 29, 2017 at 17:57
  • @michael.hor257k Yes, we can assue that there is always one singer. Commented Jul 29, 2017 at 17:58

3 Answers 3

2

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>
Sign up to request clarification or add additional context in comments.

Comments

0

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

Comments

0

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>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.