How can I convert the following XML to CSV using XSLT?
<tXML>
<Header>
<Source>XPTO</Source>
<User_ID>127</User_ID>
<Message_Type>Ship</Message_Type>
<Company_ID>105</Company_ID>
<Msg_Locale>English (United States)</Msg_Locale>
<Version>2017</Version>
</Header>
<Message>
<Ship>
<ShipSummary>
<ComName>XPTO 123</ComName>
<FacName>6</FacName>
</ShipSummary>
</Ship>
</Message>
</tXML>
tXML/Header/Source | tXML/Header/User_ID | tXML/Header/Message_Type | tXML/Header/Company_ID | tXML/Header/Msg_Locale | tXML/Header/Version | tXML/Message/Ship/ShipSummary/ComName | tXML/Message/Ship/ShipSummary/FacName |
---|---|---|---|---|---|---|---|
XPTO | 127 | Ship | 105 | English (United States) | 2017 | XPTO 123 | 6 |
How can I get the "node path" for every value, and use that as the header?
CodePudding user response:
When I run the following XSLT with xsltproc, I get your desired output (with one exception, there's an extra blank column at the end).
Much thanks to @Daniel_Haley for their solution to the general problem of printing the node path (please go vote that answer up if you like this).
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" />
<xsl:strip-space elements="*" />
<xsl:variable name="delim">;</xsl:variable>
<xsl:template match="/">
<!-- Recurse document for header -->
<xsl:copy>
<xsl:apply-templates select="node()" mode="header"/>
</xsl:copy>
<!-- Linebreak after last column in header -->
<xsl:text>
</xsl:text>
<!-- Recurse document for values -->
<xsl:copy>
<xsl:apply-templates select="node()" />
</xsl:copy>
<!-- Linebreak after last column in row -->
<xsl:text>
</xsl:text>
</xsl:template>
<xsl:template match="text()">
<xsl:copy-of select="." />
<xsl:value-of select="$delim"/>
</xsl:template>
<!-- https://stackoverflow.com/a/10112579/246801 -->
<xsl:template match="text()" mode="header">
<xsl:for-each select="ancestor::*">
<xsl:choose>
<!-- avoid beginning slash (at root) -->
<xsl:when test="position() = 1">
<xsl:value-of select="local-name()" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat('/',local-name())" />
</xsl:otherwise>
</xsl:choose>
<xsl:if test="(preceding-sibling::*|following-sibling::*)[local-name()=local-name(current())]">
<xsl:value-of select="concat('[',count(preceding-sibling::*[local-name()=local-name(current())]) 1,']')" />
</xsl:if>
</xsl:for-each>
<xsl:value-of select="$delim"/>
<!-- <xsl:apply-templates select="node()" /> -->
</xsl:template>
</xsl:stylesheet>
tXML/Header/Source | tXML/Header/User_ID | tXML/Header/Message_Type | tXML/Header/Company_ID | tXML/Header/Msg_Locale | tXML/Header/Version | tXML/Message/Ship/ShipSummary/ComName | tXML/Message/Ship/ShipSummary/FacName | |
---|---|---|---|---|---|---|---|---|
XPTO | 127 | Ship | 105 | English (United States) | 2017 | XPTO 123 | 6 |