I'd like to convert XML into CSV using an XSLT, but when applying the XSL from the SO thread titled XML To CSV XSLT against my input:
<WhoisRecord> <DomainName>127.0.0.1</DomainName> <RegistryData> <AbuseContact> <Email>abuse@iana.org</Email> <Name>Internet Corporation for Assigned Names and Number</Name> <Phone>+1-310-301-5820</Phone> </AbuseContact> <AdministrativeContact i:nil="true"/> <BillingContact i:nil="true"/> <CreatedDate/> <RawText>...</RawText> <Registrant> <Address>4676 Admiralty Way, Suite 330</Address> <City>Marina del Rey</City> <Country>US</Country> <Name>Internet Assigned Numbers Authority</Name> <PostalCode>90292-6695</PostalCode> <StateProv>CA</StateProv> </Registrant> <TechnicalContact> <Email>abuse@iana.org</Email> <Name>Internet Corporation for Assigned Names and Number</Name> <Phone>+1-310-301-5820</Phone> </TechnicalContact> <UpdatedDate>2010-04-14</UpdatedDate> <ZoneContact i:nil="true"/> </RegistryData> </WhoisRecord>
I end up with:
abuse@iana.orgInternet Corporation for Assigned Names and Number+1-310-301-5820,
,
,
,
...,
4676 Admiralty Way, Suite 330Marina del ReyUSInternet Assigned Numbers Authority90292-6695CA,
abuse@iana.orgInternet Corporation for Assigned Names and Number+1-310-301-5820,
2010-04-14,
My problem is that, the resulting transformation is missing nodes (like the DomainName element containing the IP address) and some child nodes are concatenated without commas (like the children of AbuseContact).
I'd like to see all the XML output in CSV form, and strings like: "abuse@iana.orgInternet Corporation for Assigned Names and Number+1-310-301-5820," delimited by commas.
My XSL is pretty rusty. Your help is appreciated. :)
Here's the XSL I'm using:
<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> </xsl:text> </xsl:if> </xsl:for-each> </xsl:template> </xsl:stylesheet>
I believe that you need recursive solution to approach this problem. So, you'd require something that keeps diving into the tree till it reaches a text() node. If that text() node is the actually a child of the last node, then it puts a new line. Otherwise, it just puts the value with a comma.
If the node does not has a text() node as its child, then recursively start to dig into that tree.
This is not fool proof, but it produced this result on my end with Saxon:
Hope this helps.
This simple transformation produces the wanted result:
Do note the use of:
to discard any white-space-only text nodes.
Update: AJ raised the problem that the results shoud be grouped in recirds/tuples per line. It isn't defined in the question what a record/tuple should exactly be. Therefore the current solution solves the two problems of white-space-only text nodes and of missing commas, but does not aim to grop the output into records/tuples.