1

the following codes changes xml to csv.

when it is applied to the sample data below,

 <Addy>
<Row>
<L>1</L>
<LD>Dwelling</LD>
<Th>Passage</Th>
</Row>
</ADDY>

it produces a csv file in this formatincludng the column names

 L,LD,Th
 1,Dwelling,Passage

the idea is to do away with the column names and add a comma (,) at the end of the last value such that the expected result is

1,Dwelling,passage,
1
  • Can you guarantee no value will have a comma in it? If it can happen, how would you want that output? Commented Sep 12, 2012 at 13:05

3 Answers 3

1

Remove

<xsl:for-each select="*/*/*[generate-id() = generate-id(key('field',name())[1])]">
    <xsl:value-of select="name()" />
    <xsl:if test="position() != last()">,</xsl:if>
</xsl:for-each>
<xsl:text>&#10;</xsl:text>

from the template for / and add comma:

<xsl:text>,&#10;</xsl:text>

in the template for * in row mode.

Sign up to request clarification or add additional context in comments.

Comments

1

If the elements are in the same order for every Row then you don't need any of the complex Muenchian Grouping, template modes, etc. Just a very simple stylesheet would suffice:

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

  <xsl:template match="Row">
    <xsl:apply-templates select="*" />
    <xsl:text>&#10;</xsl:text>
  </xsl:template>

  <xsl:template match="Row/*">
    <xsl:value-of select="." />
    <xsl:text>,</xsl:text>
  </xsl:template>
</xsl:stylesheet>

Running this on the following input:

<Addy>
  <Row>
    <L>1</L>
    <LD>Dwelling</LD>
    <Th>Passage</Th>
  </Row>
  <Row>
    <L>2</L>
    <LD>Foo</LD>
    <Th>Bar</Th>
  </Row>
</Addy>

produces the output you are after, including the trailing comma after the last column value:

1,Dwelling,Passage,
2,Foo,Bar,

Comments

0

Just modify the template matching / so that now it should be:

   <xsl:template match="/">
    <xsl:apply-templates select="*/*" mode="row"/>
   </xsl:template>

And if you really want a trailing comma at the end of each line produced, then replace the first occurence of:

<xsl:if test="position() != last()">,</xsl:if>

with:

<xsl:text>&#10;</xsl:text>

When only these minimal modifications are performed, the provided code becomes:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="1.0">

   <xsl:key name="field" match="/*/*/*" use="name()" />
   <xsl:output method="text"/>

   <xsl:template match="/">
    <xsl:apply-templates select="*/*" mode="row"/>
   </xsl:template>

    <xsl:template match="*" mode="row">
    <xsl:variable name="row" select="*" />
    <xsl:for-each select="/*/*/*[generate-id() = generate-id(key('field',name())[1])]">
        <xsl:variable name="name" select="name()" />
        <xsl:apply-templates select="$row[name()=$name]" mode="data" />
        <xsl:text>,</xsl:text>
    </xsl:for-each>
    <xsl:text>&#10;</xsl:text>
    </xsl:template>

    <xsl:template match="*" mode="data">
    <xsl:choose>
        <xsl:when test="contains(text(),',')">
            <xsl:text>&quot;</xsl:text>
            <xsl:call-template name="doublequotes">
                <xsl:with-param name="text" select="text()" />
            </xsl:call-template>
            <xsl:text>&quot;</xsl:text>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="." />
        </xsl:otherwise>
    </xsl:choose>
    <xsl:if test="position() != last()">,</xsl:if>
    </xsl:template>

    <xsl:template name="doublequotes">
    <xsl:param name="text" />
    <xsl:choose>
        <xsl:when test="contains($text,'&quot;')">
            <xsl:value-of select="concat(substring-before($text,'&quot;'),'&quot;&quot;')" />
            <xsl:call-template name="doublequotes">
                <xsl:with-param name="text" select="substring-after($text,'&quot;')" />
            </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="$text" />
        </xsl:otherwise>
    </xsl:choose>
 </xsl:template>
</xsl:stylesheet>

When this transformation is applied on the following XML document (note the different order of the children in the two Row elements):

<Addy>
  <Row>
    <L>1</L>
    <LD>Dwelling</LD>
    <Th>Passage</Th>
  </Row>
  <Row>
    <Th>Bar</Th>
    <LD>Foo</LD>
    <L>2</L>
  </Row>
</Addy>

the wanted, correct result is produced:

1,Dwelling,Passage,
2,Foo,Bar,

7 Comments

@lee, the output lines are already numbered -- do you mean something else?
@lee, Oh, I see. I will be able to have a look at this after work -- in approximately 9hrs from now.
@lee, I did read the question, but it is difficult to understand what you want...
@ DImitre, can you add detailed information about the above codes for better undestanding and documentation? thank you.
@lee, This is easy -- I have done just two things to your original code: 1. Removed the check for "not-last", so that a comma will be added even after the last row component now. 2. Added an <xsl:text>&#10;</xsl:text> -- which generates a NewLine character after the output of the CSV representation of every row.
|

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.