XML to CSV using XSLT
Asked Answered
C

2

-2

XML IS:

<projects>
    <project>
        <name ID="A" StartDate='2012-01-01' EndDate='2012-01-30'>Shockwave</name> 
        <language>Ruby</language> 
        <Manager ID="M1">Nathan</Manager>
    </project>
    <project>
        <name ID="B" StartDate='2012-01-01' EndDate='2012-02-30'>Other</name> 
        <language>Erlang</language>
        <Manager ID="M2">Kristi</Manager>  
    </project>
</projects>

I want to copy this XML to CSV using XSLT. There are 1000 such projects. How can I get the output Shown below into a csv:

A;2012-01-01;2012-01-30;Shockwave;Ruby;M1;Nathan|B;2012-01-01;2012-02-30;Other;Erlang;M2;Kristi|   
Cerf answered 16/5, 2013 at 18:0 Comment(2)
xml 1.0....The attributes are not static and change from one XML file to another...Cerf
I tried solution found at https://mcmap.net/q/257722/-xml-to-csv-using-xslt but is not copying the project IDCerf
B
1

Try something like this instead. It will output any attributes before the text of the element.

XML Input

<projects>
    <project>
        <name ID="A">Shockwave</name> 
        <language>Ruby</language> 
    </project>
    <project>
        <name ID="B">Other</name> 
        <language>Erlang</language>  
    </project>
</projects>

XSLT 1.0

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

    <xsl:template match="project">
        <xsl:apply-templates select="@*|node()"/>
        <xsl:text>&#xA;</xsl:text>
    </xsl:template>

    <xsl:template match="@*">
        <xsl:value-of select="."/>
        <xsl:if test="../text() or parent::project">
            <xsl:text>,</xsl:text>
        </xsl:if>
    </xsl:template>

    <xsl:template match="*[ancestor::project]">
        <xsl:apply-templates select="@*"/>
        <xsl:if test="preceding-sibling::*">
            <xsl:text>,</xsl:text>
        </xsl:if>
        <xsl:value-of select="."/>
    </xsl:template>

</xsl:stylesheet>

Output

A,Shockwave,Ruby
B,Other,Erlang

EDIT FOR NEW REQUIREMENTS

XML Input

<projects>
    <project>
        <name ID="A" StartDate='2012-01-01' EndDate='2012-01-30'>Shockwave</name> 
        <language>Ruby</language> 
        <Manager ID="M1">Nathan</Manager>
    </project>
    <project>
        <name ID="B" StartDate='2012-01-01' EndDate='2012-02-30'>Other</name> 
        <language>Erlang</language>
        <Manager ID="M2">Kristi</Manager>  
    </project>
</projects>

XSLT 1.0

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

    <xsl:template match="project">
        <xsl:apply-templates select="@*|node()"/>
        <xsl:text>|</xsl:text>
    </xsl:template>

    <xsl:template match="@*">
        <xsl:if test="count(parent::*/@*)=1 and not(parent::project)">
            <xsl:text>;</xsl:text>
        </xsl:if>
        <xsl:value-of select="."/>
        <xsl:if test="parent::project or (last())">
            <xsl:text>;</xsl:text>
        </xsl:if>
    </xsl:template>

    <xsl:template match="*[ancestor::project]">
        <xsl:apply-templates select="@*"/>
        <xsl:if test="preceding-sibling::* and not(@*)">
            <xsl:text>;</xsl:text>
        </xsl:if>
        <xsl:value-of select="."/>
    </xsl:template>

</xsl:stylesheet>

Output

A;2012-01-01;2012-01-30;Shockwave;Ruby;M1;Nathan|B;2012-01-01;2012-02-30;Other;Erlang;M2;Kristi|
Blizzard answered 17/5, 2013 at 22:47 Comment(3)
@Shiny p - If you want to update the XML input, please update it your question and not in my answer.Blizzard
-I updated my question..Is it possible to get what I have in my question? I tried what you have suggested in different ways and this XSLT doesnot give a " , "after Ruby..Please let me know If If there is away to achieve this..That will be a great help to meCerf
@shinyp - Thanks :-). I've made an update that should work. I didn't have much time, so let me know if something isn't working correctly.Blizzard
W
1

Use the solution you linked to, but change the line

<xsl:for-each select="child::*">

to

<xsl:for-each select="child::*|attribute::*">

This will select attributes as well as nodes.

Waylon answered 16/5, 2013 at 18:19 Comment(2)
@shinyp, sorry about that, please see edit. That's what I get for posting untested code!Waylon
How can I Pull Data If Name in the above example has attribute?Cerf
B
1

Try something like this instead. It will output any attributes before the text of the element.

XML Input

<projects>
    <project>
        <name ID="A">Shockwave</name> 
        <language>Ruby</language> 
    </project>
    <project>
        <name ID="B">Other</name> 
        <language>Erlang</language>  
    </project>
</projects>

XSLT 1.0

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

    <xsl:template match="project">
        <xsl:apply-templates select="@*|node()"/>
        <xsl:text>&#xA;</xsl:text>
    </xsl:template>

    <xsl:template match="@*">
        <xsl:value-of select="."/>
        <xsl:if test="../text() or parent::project">
            <xsl:text>,</xsl:text>
        </xsl:if>
    </xsl:template>

    <xsl:template match="*[ancestor::project]">
        <xsl:apply-templates select="@*"/>
        <xsl:if test="preceding-sibling::*">
            <xsl:text>,</xsl:text>
        </xsl:if>
        <xsl:value-of select="."/>
    </xsl:template>

</xsl:stylesheet>

Output

A,Shockwave,Ruby
B,Other,Erlang

EDIT FOR NEW REQUIREMENTS

XML Input

<projects>
    <project>
        <name ID="A" StartDate='2012-01-01' EndDate='2012-01-30'>Shockwave</name> 
        <language>Ruby</language> 
        <Manager ID="M1">Nathan</Manager>
    </project>
    <project>
        <name ID="B" StartDate='2012-01-01' EndDate='2012-02-30'>Other</name> 
        <language>Erlang</language>
        <Manager ID="M2">Kristi</Manager>  
    </project>
</projects>

XSLT 1.0

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

    <xsl:template match="project">
        <xsl:apply-templates select="@*|node()"/>
        <xsl:text>|</xsl:text>
    </xsl:template>

    <xsl:template match="@*">
        <xsl:if test="count(parent::*/@*)=1 and not(parent::project)">
            <xsl:text>;</xsl:text>
        </xsl:if>
        <xsl:value-of select="."/>
        <xsl:if test="parent::project or (last())">
            <xsl:text>;</xsl:text>
        </xsl:if>
    </xsl:template>

    <xsl:template match="*[ancestor::project]">
        <xsl:apply-templates select="@*"/>
        <xsl:if test="preceding-sibling::* and not(@*)">
            <xsl:text>;</xsl:text>
        </xsl:if>
        <xsl:value-of select="."/>
    </xsl:template>

</xsl:stylesheet>

Output

A;2012-01-01;2012-01-30;Shockwave;Ruby;M1;Nathan|B;2012-01-01;2012-02-30;Other;Erlang;M2;Kristi|
Blizzard answered 17/5, 2013 at 22:47 Comment(3)
@Shiny p - If you want to update the XML input, please update it your question and not in my answer.Blizzard
-I updated my question..Is it possible to get what I have in my question? I tried what you have suggested in different ways and this XSLT doesnot give a " , "after Ruby..Please let me know If If there is away to achieve this..That will be a great help to meCerf
@shinyp - Thanks :-). I've made an update that should work. I didn't have much time, so let me know if something isn't working correctly.Blizzard

© 2022 - 2024 — McMap. All rights reserved.