使用 XSLT 将 XML 转换为 CSV(节点路径为 header)

Convert XML to CSV (with node path as header) using XSLT

如何使用 XSLT 将以下 XML 转换为 CSV?

<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

如何获取每个值的“节点路径”,并将其用作 header?

当我 运行 使用 xsltproc 的以下 XSLT 时,我得到了您想要的输出(除了一个例外,末尾有一个额外的空白列)。

非常感谢@Daniel_Haley their solution 打印节点路径的一般问题(如果你喜欢这个,请投票回答)。

<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>&#xA;</xsl:text>
        
        <!-- Recurse document for values -->
        <xsl:copy>
            <xsl:apply-templates select="node()" />
        </xsl:copy>

        <!-- Linebreak after last column in row -->
        <xsl:text>&#xA;</xsl:text>
    </xsl:template>

    <xsl:template match="text()">
        <xsl:copy-of select="." />
        <xsl:value-of select="$delim"/>
    </xsl:template>

    <!--  -->
    <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