0
votes

I am trying to transform my XML document to CSV through XSLT. Not able to achieve the desired result so far.

XML as below:

<projects>
  <project>
    <name>Shockwave</name>
    <language>Ruby</language>
    <owner>Brian May</owner>
    <state>New</state>
    <startDate>31/10/2008 0:00:00</startDate>
  </project>
  <project>
    <name>Other</name>
    <language>Erlang</language>
    <owner>Takashi Miike</owner>
    <state> Canceled </state>
    <startDate>07/11/2008 0:00:00</startDate>
  </project>
  </projects>

XSLT as below:

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text" encoding="iso-8859-1"/>

  <xsl:strip-space elements="*" />

  <xsl:template match="/*/child::*">
    <xsl:for-each select="child::*">
      <xsl:if test="position() != last()">
        <xsl:value-of select="normalize-space(.)"/>,
      </xsl:if>
      <xsl:if test="position()  = last()">
        <xsl:value-of select="normalize-space(.)"/><xsl:text>&#xD;</xsl:text>
      </xsl:if>
    </xsl:for-each>
  </xsl:template>

</xsl:stylesheet>

And Output as below: enter image description here

While requirement is to transform to CSV as below: Header is first row. and then newline after startDate.

name,language,owner,state,startDate

Shockwave, Ruby, Brian May,New,31/10/2008 0:00:00

Other,Erlang,Takashi Miike,Canceled,07/11/2008 0:00:00

enter image description here

1

1 Answers

2
votes

The problem is with this statement

  <xsl:if test="position() != last()">
    <xsl:value-of select="normalize-space(.)"/>,
  </xsl:if>

You are outputting a comma, but the line break after the comma will also be output. XSLT will only ignore white-space if the whole text node is whitespace. As soon as you include a non-whitespace character, it will output your indentation too!

So, you need to change it to this...

  <xsl:if test="position() != last()">
    <xsl:value-of select="normalize-space(.)"/><xsl:text>,</xsl:text>
  </xsl:if>

Having said that, you can simplify your XSLT a little bit more. Try this instead

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text" encoding="iso-8859-1"/>

  <xsl:strip-space elements="*" />

  <xsl:template match="/*/child::*">
    <xsl:for-each select="child::*">
      <xsl:if test="position() > 1">
        <xsl:text>,</xsl:text>
      </xsl:if>
      <xsl:value-of select="normalize-space(.)"/>
    </xsl:for-each>
    <xsl:text>&#xD;</xsl:text>
  </xsl:template>
</xsl:stylesheet>

EDIT: If you wanted to output a header row, add this template to the XSLT

<xsl:template match="/*">
   <xsl:for-each select="*[1]/*">
      <xsl:if test="position() > 1">
        <xsl:text>,</xsl:text>
      </xsl:if>
      <xsl:value-of select="local-name()"/>
   </xsl:for-each>
   <xsl:text>&#xD;</xsl:text>
   <xsl:apply-templates />
</xsl:template>