0

I'm trying to transform an XML document into CSV, which is something I have done successfully before, however my current XML is structured differently in the sense that all of the child node elements are named the same (value). Here's the XML:

    <?xml version="1.0" encoding="utf-8"?>
<dataset  xmlns="http://developer.cognos.com/schemas/xmldata/1/" xmlns:xs="http://www.w3.org/2001/XMLSchema-instance">
    <metadata>
          <item name="Employee Number" type="xs:string" length="20"/>
          <item name="Last Name" type="xs:string" length="202"/>
          <item name="First Name" type="xs:string" length="202"/>
    </metadata>
    <data>
        <row>
            <value>000056</value>
            <value>Atkinson</value>
            <value>Joan</value>
        </row>
        <row>
            <value>000061</value>
            <value>Bolton</value>
            <value>Larry</value>
        </row>
        <row>
            <value>000092</value>
            <value>Hadley</value>
            <value>Charles</value>
        </row>
    </data>
</dataset>

What each node represents is defined in the section above. That said, here is my desired output:

000056,Atkinson,Joan
000061,Bolton,Larry
000092,Hadley,Charles

And this is the XSLT I've started with:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
  <xsl:output method="xml" omit-xml-declaration="yes" indent="no"/>
  <xsl:strip-space elements="*" />

  <xsl:template match="/data">
    <xsl:for-each select="row">
      <!-- Employee Number -->
      <xsl:text>,</xsl:text>
      <!-- Last Name -->
      <xsl:text>,</xsl:text>
      <!-- First Name -->
      <xsl:text>,</xsl:text>
      <!-- new line -->
      <xsl:text>&#10;</xsl:text>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

I haven't yet figured out how to properly code the elements for each of the three data elements, so I would expect to actually see only two commas on each of three lines of output. However, this is what I'm actually getting:

000056AtkinsonJoan000061BoltonLarry000092HadleyCharles

It's ignoring everything I have within the for-each loop and just outputting all of the data within the entire element. I also discovered that even if I change the select="row" attribute to select="zzz" (or any other value) I still get the same result. So, where did I go off the rails?

PongGod
  • 829
  • 1
  • 11
  • 19
  • 1
    You need to take the namespace in the input XML into acount when writing your XSLT or XPath patterns and expressions. http://stackoverflow.com/questions/1344158/xslt-with-xml-source-that-has-a-default-namespace-set-to-xmlns – Martin Honnen Feb 18 '15 at 20:49

2 Answers2

1

It's because you have a default namespace. The results you are seeing are from the built-in templates. You should declare the namespace with a prefix and use that prefix in your XPaths.

Example...

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:x="http://developer.cognos.com/schemas/xmldata/1/">
    <xsl:output method="text"/>
    <xsl:strip-space elements="*" />

    <xsl:template match="x:row">
        <xsl:apply-templates/>
        <xsl:text>&#xA;</xsl:text>
    </xsl:template>

    <xsl:template match="x:value">
        <xsl:if test="not(position()=1)">
            <xsl:text>,</xsl:text>
        </xsl:if>
        <xsl:value-of select="."/>
    </xsl:template>
</xsl:stylesheet>
Daniel Haley
  • 51,389
  • 6
  • 69
  • 95
1

This is an issue caused by the additional namespace in the input XML

xmlns="http://developer.cognos.com/schemas/xmldata/1/"

You can add the namespace with a prefix to the XSLT, e.g. as

xmlns:c="http://developer.cognos.com/schemas/xmldata/1/"

and then match the elements of the XML accordingly:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:c="http://developer.cognos.com/schemas/xmldata/1/"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:output method="xml" omit-xml-declaration="yes" indent="no"/>
<xsl:strip-space elements="*" />
   <xsl:template match="//c:data">
    <xsl:for-each select="c:row">
      <xsl:for-each select="c:value">
        <xsl:value-of select="."/>
        <xsl:if test="position() != last()">
          <xsl:text>,</xsl:text>
        </xsl:if>
      </xsl:for-each>
      <xsl:text>&#10;</xsl:text>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

Output:

000056,Atkinson,Joan
000061,Bolton,Larry
000092,Hadley,Charles

I've added a second for-each loop for the values, otherwise you can retrieve the value using <xsl:value-of select="c:value[1]"/> for the first value etc.

Saved example: http://xsltransform.net/pPqsHTu

matthias_h
  • 11,356
  • 9
  • 22
  • 40