I am writing a program in java( pre-junior), I really need help with xslt transformation. It is necessary to make a csv file from xml.
I got this xslt filter:
<?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" omit-xml-declaration="yes" indent="no"/>
<xsl:template match="node()" name="conv">
<xsl:call-template name="loop"/>
</xsl:template>
<xsl:template name="loop">
<xsl:for-each select="./*[count(*) = 0]">
<xsl:value-of select="."/>
<xsl:if test="position() != last()">
<xsl:text>,</xsl:text>
</xsl:if>
<xsl:if test="position() = last()">
<xsl:text>,</xsl:text>
</xsl:if>
</xsl:for-each>
<xsl:text>
</xsl:text>
<xsl:for-each select="./*[(count(*) != 0) and (name()!='PARAMETRS')] ">
<xsl:call-template name="loop"/>
</xsl:for-each>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
Source xml:
<Integration>
<PARAMETRS>
<ID>AZD</ID>
<DATE>2020-01-01</DATE>
</PARAMETRS>
<ORG>
<Thing>
<object>10220</object>
<type>U</type>
<dyn>
<items>
<val>988009</val>
<datebegin>2019-12-12</datebegin>
</items>
</dyn>
</Thing>
<Thing>
<object>10221</object>
<type>U</type>
<dyn>
<items>
<val>988010</val>
<datebegin>2019-12-13</datebegin>
</items>
<items>
<val>988011</val>
<datebegin>2019-12-14</datebegin>
</items>
</dyn>
</Thing>
</ORG>
</Integration>
In the output, I get comma-separated lines, and a few more lines (those same items) with the values below. and can't figure out how to concatenate the values ...
I would do it via value-of select = "concat" but my may have several dyn (1, 2, 3 ...), hence this is not suitable.
The output needs a csv separated by commas.
Please advise how to concatenate the item with its parent? Or there are simpler ways to parse xml with a different number of subsections(childs).
Expected output:
10220,U,988009,2019-12-12
10221,U,988010,2019-12-13,988011,2019-12-14
The output you show can be easily obtained using the following stylesheet:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/Integration">
<xsl:for-each select="ORG/Thing">
<xsl:value-of select="object"/>
<xsl:text>,</xsl:text>
<xsl:value-of select="type"/>
<xsl:text>,</xsl:text>
<xsl:for-each select="dyn/items">
<xsl:value-of select="val"/>
<xsl:text>,</xsl:text>
<xsl:value-of select="datebegin"/>
<xsl:if test="position() != last()">
<xsl:text>,</xsl:text>
</xsl:if>
</xsl:for-each>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Note that the output has a set of columns for each items; this is not an ideal CSV structure.
If you can use XSLT 2.0, it opens up new powerful functionality.
Oracle XML Developer Kit (XDK) supports XSLT 2.0
Here is the link: Using the XSLT Processor for Java
The approach below is doing the following:
Using string-join() function to concatenate all child elements values
on a different hierarchy level via .//*/(text()[1] expression.
xs:token casting removes white spaces.
XPath predicate [. != ''] removes empty sequence members.
XSLT 2.0
<?xml version='1.0'?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="text"/>
<xsl:template match="/Integration">
<xsl:for-each select="ORG/Thing">
<xsl:value-of select="string-join((.//*/(text()[1] cast as xs:token?))[. != ''],',')"/>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Output
10220,U,988009,2019-12-12
10221,U,988010,2019-12-13,988011,2019-12-14
Based on the Marting Honnen great tip, here is even more concise XSLT 2.0 version without any loop.
XSLT 2.0
<?xml version="1.0"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="text"/>
<xsl:template match="/Integration">
<xsl:value-of select="ORG/Thing/string-join((.//*/(text()[1] cast as xs:token?))[. != ''],',')" separator="
"/>
</xsl:template>
</xsl:stylesheet>
Related
Hi I'm trying to transform an XML to another XML using xsl. The XSL transformation is working fine when tested in Oxygen developer and in VM also.
But still I'm getting an Warning like "Illegal value used for attribute name" at line number 20 and 23.
XSL which i used is:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:cl="http://tyryt.com/abc/abc_order"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:java="http://xml.apache.org/xslt/java"
exclude-result-prefixes="cl xsd java" version="1.0">
<xsl:output indent="yes" method="xml" encoding="UTF-8" omit-xml-declaration="no"/>
<xsl:template name="generateInsightsHeader">
<xsl:for-each select="/">
<xsl:element name="pon">
<xsl:value-of select="*/cl:HDR/cl:PON"/>
</xsl:element>
<xsl:element name="version">
<xsl:value-of select="*/cl:HDR/cl:VER"/>
</xsl:element>
</xsl:for-each>
</xsl:template>
<xsl:template name="copyWholeXml" match="*">
<xsl:if test="local-name() != 'brmserrorxml'">
<xsl:element name="{local-name()}">
<xsl:for-each select="#*">
<xsl:if test="../#protocoltype != ''">
<xsl:attribute name="{local-name()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:if>
</xsl:for-each>
<xsl:apply-templates/>
</xsl:element>
</xsl:if>
</xsl:template>
<xsl:template match="/">
<IntermediateXml xmlns:json="http://json.org/">
<xsl:choose>
<xsl:when test="cl:SAMPLE_ORDER">
<xsl:call-template name="generateInsightsHeader"/>
<xsl:call-template name="copyWholeXml"/>
</xsl:when>
</xsl:choose>
</IntermediateXml>
</xsl:template>
</xsl:stylesheet>
and Sample XML i used is:
<?xml version="1.0"?>
<SAMPLE_ORDER class="1" index="0" xmlns="http://tyryt.com/abc/abc_order" service="SAMPLE_ORDER">
<header>
<interfaceid>INTERFACE_REQUIRED</interfaceid>
<actionrequired>Transmit</actionrequired>
</header>
<HDR>
<MESSAGE_ID>229364ASE</MESSAGE_ID>
<DATE>2017-09-04T12:29:11+05:30</DATE>
<PON>229364ASE10</PON>
<VER>01</VER>
</HDR>
<LOCAL_MESSAGE>
<LOCAL_ADMIN>
<LOCQTY>111</LOCQTY>
<AN>JHGKJ679868</AN>
<CC>005F</CC>
<AUTHORIZATION>
<DDD>20171228</DDD>
<ACTL>01234567899</ACTL>
</AUTHORIZATION>
</LOCAL_ADMIN>
<LSR_BILL>
<ACNA>WEE</ACNA>
</LSR_BILL>
<CONTACT>
<INIT>ASDDFAD</INIT>
<INIT_TEL_NO>2543654576</INIT_TEL_NO>
<INIT_EMAIL>abc#gmail.com</INIT_EMAIL>
</CONTACT>
</LOCAL_MESSAGE>
<LS>
<LS_SVC_DET>
<SVC_DET_GRP>
<LOCNUM>145</LOCNUM>
<LNUM>78945</LNUM>
<LNA>D</LNA>
<ECCKT>98698KJGKUJGUKJH</ECCKT>
</SVC_DET_GRP>
</LS_SVC_DET>
</LS>
</SAMPLE_ORDER>
For Transformation i'm using Xalan jar.
Please help me to solve this problem.
Thanks in Advance
The root node has no name, or more precisely, the name of the document's root node is the empty string ''. So when you call your template "copyWholeXml" and the current context node is the root node, local-name() evaluates to '', and <xsl:element name="{local-name()}"> is invalid, because you cannot generate a new XML element with an empty string as the name.
When you call your template "copyWholeXml" make sure that you are not entering the named template with the document root as the context node
I have a problem. I have an xml file as below :
Main.xml
<?xml version="1.0" encoding="ISO-8859-1"?>
<msrsw>
<software>
<chapter>
<name> Hello world</sample>
<xref type="xml">C:\ABC\NestedXML.xml</xref>
</chapter>
</software>
</msrsw>
NestedXml.xml :
<?xml version="1.0" encoding="ISO-8859-1"?>
<msrsw>
<software>
<chapter>
<name> Nested XML </sample>
<age>14</age>
<country>Canada</country>
</chapter>
</software>
</msrsw>
I am using Apache FOP to produce PDF document (Using Java). FOP needs src and dest params. Src being Main.xml.
My XSLT is as below:
<xsl:param name="xmlFileName" />
<xsl:param name="XMLFile" select="document($xmlFileName)"/>
<xsl:template name="Chapters_1_2_Template">
<xsl:apply-templates select="$XMLFile/*" mode="chapter" />
</xsl:template>
<xsl:template match="node()" mode="chapter">
<xsl:for-each select="node()">
<xsl:if test="current()[name() = 'xref']">
<xsl:apply-templates select="current()[name() = 'xref']" mode="x" />
</xsl:if>
<xsl:if test="current()[name() = 'name']">
<xsl:apply-templates select="current()[name() = 'name']" mode="name" />
</xsl:if>
<xsl:if test="current()[name() = 'age']">
<xsl:apply-templates select="current()[name() = 'age']" mode="age" />
</xsl:if>
</xsl:for-each>
</xsl:template>
<xsl:template match="name" mode="name">
<xsl:value-of select="current()" />
</xsl:template>
<xsl:template match="age" mode="age">
<xsl:value-of select="current()" />
</xsl:template>
<xsl:template match="country" mode="country">
<xsl:value-of select="current()" />
</xsl:template>
<xsl:template match="xref" mode="x">
<xsl:param name="XMLFile" select="document(current())"/>
<xsl:call-template name="Chapters_1_2_Template" />
</xsl:template>
When I execute the above code, i get the following error in the line <xsl:apply-templates select="$XMLFile/*" mode="chapter" />:
Invalid token XMLFile.
When I remove $XMLFile - <xsl:apply-templates select="*" mode="chapter" /> , it works fine for me with Main.xml file's content.
When I just display the value of current() in xref template, it is parses the NestedXml.xml file and displayes string values in PDF.
Expected Output is:
Hello world
Nested XML
14
Canada
My requirement is to embed the node-set of NestedXML.xml within the<xref></xref> tags and recursively applyTemplates on that node-set.
But the document() function gives me the parsed String content of NestedXml.xml.
I do not want to use the document() function and just get the String value of NestedXml.xml. I need to parse all the tags of NestedXml.xml using the same XSLT.
Please suggest me where am I going wrong. Is this approach right? Is there any other way to do this?
Or it is not possible to do in this way using XSLT and that XSLT allows including only the parsed String values?
If it is that you want just some text nodes, the stylesheet needn't be that complex. Try this simpler stylesheet:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<xsl:output method="text" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:apply-templates select="msrsw/software/chapter/name"/>
<xsl:apply-templates select="document(msrsw/software/chapter/xref)/*"/>
</xsl:template>
<xsl:template match="text()">
<xsl:value-of select="normalize-space()"/>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
I have a requirement of masking few fields in XML of CDATA inside XML with XSLT.
So the resultant XML should be same like the input XML but few fileds are masked with XSLT.
I followed this link which is masking as expected but producing XML is in different format.
I tried many other solutions from SO, they are almost outputing the new XML/HTML in other format which is different from the input XML.
Please check the following example for better understading.
Input XML with CDATA content.
<XML>
<LogLevel>info</LogLevel>
<Content><![CDATA[ <Msg>
<AccountNo>2701000098983</AccountNo>
<ApplName>Testing</ApplName>
</Msg>]]></Content>
<Date>20140909</Date>
</XML>
Output XML should be:
<XML>
<LogLevel>info</LogLevel>
<Content><![CDATA[ <Msg>
<AccountNo>XXXXXXXXXX983</AccountNo>
<ApplName>Testing</ApplName>
</Msg>]]></Content>
<Date>20140909</Date>
</XML>
Edit:
I used the following XSLT
<xsl:template match="node()">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="text()">
<xsl:choose>
<xsl:when test="contains(.,'<AccountNo>')">
<!-- This is the CDATA that I want to mask and write back out as CDATA -->
<xsl:variable name="tcontent">
<xsl:value-of
select="substring-after(substring-before(.,'</AccountNo>'),'<AccountNo>') " />
</xsl:variable>
<xsl:text disable-output-escaping="yes"><![CDATA[<AccountNo></xsl:text>
<xsl:call-template name="maskVariable">
<xsl:with-param name="tvar" select="$tcontent" />
</xsl:call-template>
<xsl:text disable-output-escaping="yes"></AccountNo>]]></xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:copy />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="maskVariable">
<xsl:param name="tvar" />
<xsl:variable name="length" select="string-length($tvar)" />
<xsl:choose>
<xsl:when test="$length > 3">
<xsl:value-of
select="concat ('************', substring($tvar,$length - 1, 2))" />
</xsl:when>
<xsl:when test="$length > 1">
***
</xsl:when>
<xsl:otherwise />
</xsl:choose>
</xsl:template>
Output of using this XSLT is :
<LogLevel>info</LogLevel>
<Content><![CDATA[<AccountNo>************02</AccountNo>]]></Content>
<Date>20140909</Date>
Here in output, only masked output of is displaying.
How to make other part of the code to get displayed ?
Please give me some idea how to do it ?
Any help is highly appreciated.
Why don't you try:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Content">
<xsl:copy>
<xsl:value-of select="substring-before(.,'<AccountNo>')" />
<xsl:text><AccountNo></xsl:text>
<xsl:variable name="acct-num" select="substring-before(substring-after(.,'<AccountNo>'), '</AccountNo>')" />
<xsl:value-of select="concat('************', substring($acct-num, string-length($acct-num) - 2))" />
<xsl:text></AccountNo></xsl:text>
<xsl:value-of select="substring-after(.,'</AccountNo>')" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Applied to your input, the result will be:
<?xml version="1.0" encoding="UTF-8"?>
<XML>
<LogLevel>info</LogLevel>
<Content> <Msg>
<AccountNo>************983</AccountNo>
<ApplName>Testing</ApplName>
</Msg></Content>
<Date>20140909</Date>
</XML>
Alternatively, you could use:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" cdata-section-elements="Content"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Content">
<xsl:copy>
<xsl:variable name="content">
<xsl:value-of select="substring-before(.,'<AccountNo>')" />
<xsl:text><AccountNo></xsl:text>
<xsl:variable name="acct-num" select="substring-before(substring-after(.,'<AccountNo>'), '</AccountNo>')" />
<xsl:value-of select="concat('************', substring($acct-num, string-length($acct-num) - 2))" />
<xsl:text></AccountNo></xsl:text>
<xsl:value-of select="substring-after(.,'</AccountNo>')" />
</xsl:variable>
<xsl:value-of select="$content" disable-output-escaping="yes"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
to produce:
<?xml version="1.0" encoding="UTF-8"?>
<XML>
<LogLevel>info</LogLevel>
<Content><![CDATA[ <Msg>
<AccountNo>************983</AccountNo>
<ApplName>Testing</ApplName>
</Msg>]]></Content>
<Date>20140909</Date>
</XML>
although this might not work with every processor (tested to work with Xalan 2.7.1: http://xsltransform.net/jyH9rMk).
The stuff inside the CDATA section is XML disguised as text. XSLT is good at transforming XML, it's not so good at transforming text, especially text with a complex grammar. So my approach would be: extract the text from the outer XML document, parse it as XML, transform it using XSLT (real XSLT that works on nodes rather than on markup), serialize it back to text, then stuff the text back into the original (outer) XML document.
Raw XSLT 1.0 can't do this within a single stylesheet. You need the functions parse() and serialize() to turn lexical XML into a node tree, and back again. These are available as extensions in some processors (such as Saxon), they become available as standard functions in XPath 3.0, and they can be written as simple extension functions (e.g. in Javascript) code in most other processors.
I know there are a lot of questions on this, but I am stuck on the problem of converting xml to csv using xslt. I used an example xslt for testing:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:variable name="delimiter" select="','"/>
<xsl:key name="field" match="sObject/*" use="name()"/>
<xsl:template match="/">
<xsl:for-each select="/*/*/*[generate-id()=generate-id(key('field', name())[1])]">
<xsl:value-of select="name()"/>
<xsl:if test="position() != last()">
<xsl:value-of select="$delimiter"/>
</xsl:if>
</xsl:for-each>
<xsl:text>
</xsl:text>
<xsl:for-each select="/*/sObject">
<xsl:variable name="property" select="." />
<xsl:for-each select="$property/*">
<xsl:variable name="value" select="." />
<xsl:value-of select="$value"/>
<xsl:if test="position() != last()">
<xsl:value-of select="$delimiter"/>
</xsl:if>
<xsl:if test="position() = last()">
<xsl:text>
</xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
This code works well on this:
<?xml version="1.0" encoding="ISO-8859-1"?>
<sObjects>
<sObject>
<Name>Raagu</Name>
<BillingStreet>Hoskote</BillingStreet>
</sObject>
<sObject>
<Name>Rajath</Name>
<BillingStreet>BTM</BillingStreet>
<age>25</age>
</sObject>
<sObject>
<Name>Sarath</Name>
<BillingStreet>Murgesh</BillingStreet>
<location>Bangalore</location>
</sObject>
</sObjects>
The example I found on SO, but could anyone give me a hint on how to process an xml, which has more depth in it? Say:
<?xml version="1.0" encoding="ISO-8859-1"?>
<sObjects>
<sObject>
<Person>
<Name>Raagu</Name>
<BillingStreet>Hoskote</BillingStreet>
</Person>
<Country>Germany</Country>
</sObject>
<sObjects>
I will be very grateful for any hint on this. Thanks in advance.
If you can assuming the left nodes always correspond to fields in your CSV output, you should consider redefining the field key like-so
<xsl:key name="field" match="*[not(*)]" use="local-name()"/>
(I am using "local-name()" here so that it can cope with XML that involves namespaces)
Then, define a variable to get the distinct field names
<xsl:variable
name="fields"
select="//*[not(*)][generate-id()=generate-id(key('field', local-name())[1])]" />
Here //* will look for elements at any depth in the XML. You can then easily iterate over this variable to output the header row as currently. So far so good.
However, despite you saying the current XSLT is working well, it isn't. Because of this line:
<xsl:for-each select="$property/*">
This is assuming each sObject has the same number of child elements (i.e. it has all fields present, and in the right order), which in your XML sample is not the case.
Firstly, I would redefine the property variable to be all 'leaf' elements under the current element, at any depth.
<xsl:variable name="properties" select=".//*[not(*)]" />
Then, you need to iterate over the fields variable, not the properties element
<xsl:for-each select="$fields">
The properties element is then used to find the element within it that matches the current field name, like so
<xsl:value-of select="$properties[local-name()=local-name(current())]"/>
Try the following XSLT then
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:variable name="delimiter" select="','"/>
<xsl:key name="field" match="*[not(*)]" use="local-name()"/>
<xsl:variable name="fields" select="//*[not(*)][generate-id()=generate-id(key('field', local-name())[1])]" />
<xsl:template match="/">
<xsl:for-each select="$fields">
<xsl:value-of select="local-name()"/>
<xsl:if test="position() != last()">
<xsl:value-of select="$delimiter"/>
</xsl:if>
</xsl:for-each>
<xsl:text>
</xsl:text>
<xsl:for-each select="/*/*">
<xsl:variable name="properties" select=".//*[not(*)]" />
<xsl:for-each select="$fields">
<xsl:value-of select="$properties[local-name()=local-name(current())]"/>
<xsl:if test="position() != last()">
<xsl:value-of select="$delimiter"/>
</xsl:if>
<xsl:if test="position() = last()">
<xsl:text>
</xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
This should work on both your XML samples. Also note there is not a mention of sObject in sight (The assumption being the children of the root element correspond to the records, and the leaf elements correspond to fields)
Do note, the XSLT uses a technique called Muenchian Grouping to determine the distinct field names. Read about this, and learn it well.
I am trying to convert XML to CSV using XSLT. I have seen many similar posts on this site, but wanted to mention specific requirement and the issue I am seeing in my code.
This is my well formed XML file content. Please note, I am getting this XML from the database as a string, so it is not formatted.
XML file
<myxml ver="1.0"><name>smile</name><email>test#gmail.com</email><phone_number/><job_title>Engineer</job_title><company_name>Stack Overflow</company_name><submission-datestamp>3/14/11 8:29 PM</submission-datestamp></myxml>
Here is the XSLT file
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="myxml">
<xsl:for-each select="*" >
<xsl:if test="position() != last()">
<xsl:value-of select="normalize-space(.)"/>,
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
I am getting below CSV format content:
smile,
test#gmail.com,
,
Engineer,
Stack Overflow,
3/14/11 8:29 PM
Requirement is:
I need to display the header first
Comma separated list as in
smiletest#gmail.com,EngineerStack Overflow,3/14/11 8:29 PM
Can you please help me fix my code? The problem is in XSLT file.
You are getting the extra carriage returns because you have mixed-content inside of the template. The stylesheet assumes that the whitespace following the comma is significant. You can prevent that by putting your text inside of <xsl:text>. That tells the stylesheet which text is explicitly part of the output, so if there is nothing else left but whitespace, it is assumed to be insignificant.
I reworked the stylesheet slightly to be more of a "push style" stylesheet using xsl:apply-templates instead of xsl:for-each, and also added carriage-return at the end of each line:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:apply-templates mode="header"/>
<xsl:apply-templates />
</xsl:template>
<xsl:template match="myxml/*" mode="header">
<xsl:value-of select="local-name()"/>
<xsl:choose>
<xsl:when test="position() = last()">
<xsl:text>
</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>,</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="myxml/*">
<xsl:value-of select="normalize-space(.)"/>
<xsl:choose>
<xsl:when test="position() = last()">
<xsl:text>
</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>,</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Produces the following output:
name,email,phone_number,job_title,company_name,submission-datestamp
smile,test#gmail.com,,Engineer,Stack Overflow,3/14/11 8:29 PM