使用 XSLT 将 XML 转换为 CSV

我有以下 XML 文档:

<projects>
<project>
<name>Shockwave</name>
<language>Ruby</language>
<owner>Brian May</owner>
<state>New</state>
<startDate>31/10/2008 0:00:00</startDate>
</project>
<project>
<name>Other</name>
<language>Erlang</language>
<owner>Takashi Miike</owner>
<state> Canceled </state>
<startDate>07/11/2008 0:00:00</startDate>
</project>
...

我想从转换(XSLT)结果中得到这个结果:

Shockwave,Ruby,Brian May,New,31/10/2008 0:00:00
Other,Erlang,Takashi Miike,Cancelled,07/11/2008 0:00:00

有人知道用 XSLT 来实现这个吗? 我使用.net 以防万一。

146306 次浏览

找到一个 XML 转换样式表 给你(返回机器链接,站点本身是德语)

这里添加的样式表可能会有所帮助:

<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>&#xD;</xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:template>


</xsl:stylesheet>

也许您希望删除 xsl: if 标记中的引号,这样它就不会将您的值放入引号中,这取决于您希望在哪里使用 CSV 文件。

下面是一个带有可配置参数的版本,您可以通过编程方式设置这些参数:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="utf-8" />


<xsl:param name="delim" select="','" />
<xsl:param name="quote" select="'&quot;'" />
<xsl:param name="break" select="'&#xA;'" />


<xsl:template match="/">
<xsl:apply-templates select="projects/project" />
</xsl:template>


<xsl:template match="project">
<xsl:apply-templates />
<xsl:if test="following-sibling::*">
<xsl:value-of select="$break" />
</xsl:if>
</xsl:template>


<xsl:template match="*">
<!-- remove normalize-space() if you want keep white-space at it is -->
<xsl:value-of select="concat($quote, normalize-space(), $quote)" />
<xsl:if test="following-sibling::*">
<xsl:value-of select="$delim" />
</xsl:if>
</xsl:template>


<xsl:template match="text()" />
</xsl:stylesheet>

xsl:stylesheet可以使用指定的列标题列表,并确保行将被正确排序。它需要 XSLT 2.0版。

<?xml version="1.0"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:csv="csv:csv">
<xsl:output method="text" encoding="utf-8"/>
<xsl:strip-space elements="*"/>


<xsl:variable name="delimiter" select="','"/>


<csv:columns>
<column>name</column>
<column>sublease</column>
<column>addressBookID</column>
<column>boundAmount</column>
<column>rentalAmount</column>
<column>rentalPeriod</column>
<column>rentalBillingCycle</column>
<column>tenureIncome</column>
<column>tenureBalance</column>
<column>totalIncome</column>
<column>balance</column>
<column>available</column>
</csv:columns>


<xsl:template match="/property-manager/properties">
<!-- Output the CSV header -->
<xsl:for-each select="document('')/*/csv:columns/*">
<xsl:value-of select="."/>
<xsl:if test="position() != last()">
<xsl:value-of select="$delimiter"/>
</xsl:if>
</xsl:for-each>
<xsl:text>&#xa;</xsl:text>
    

<!-- Output rows for each matched property -->
<xsl:apply-templates select="property"/>
</xsl:template>


<xsl:template match="property">
<xsl:variable name="property" select="."/>
    

<!-- Loop through the columns in order  -->
<xsl:for-each select="document('')/*/csv:columns/*">
<!-- Extract the column name and value  -->
<xsl:variable name="column" select="."/>
<xsl:variable name="value" select="$property/*[name() = $column]"/>
        

<!-- Quote the value if required -->
<xsl:choose>
<xsl:when test="contains($value, '&quot;')">
<xsl:variable name="x" select="replace($value, '&quot;',  '&quot;&quot;')"/>
<xsl:value-of select="concat('&quot;', $x, '&quot;')"/>
</xsl:when>
<xsl:when test="contains($value, $delimiter)">
<xsl:value-of select="concat('&quot;', $value, '&quot;')"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$value"/>
</xsl:otherwise>
</xsl:choose>
        

<!-- Add the delimiter unless we are the last expression -->
<xsl:if test="position() != last()">
<xsl:value-of select="$delimiter"/>
</xsl:if>
</xsl:for-each>
    

<!-- Add a newline at the end of the record -->
<xsl:text>&#xa;</xsl:text>
</xsl:template>


</xsl:stylesheet>

这个 CsvEscape函数是 XSLT 1.0,转义列值 ,"和新行(如 RFC 4180或 Excel)。它利用了可以递归调用 XSLT 模板的事实:

  • 模板 EscapeQuotes用2个双引号替换所有双引号,从字符串开始递归。
  • 模板 CsvEscape检查文本是否包含逗号或双引号,如果包含逗号或双引号,则用一对双引号包围整个字符串,并为字符串调用 EscapeQuotes

示例用法: xsltproc xmltocsv.xslt file.xml > file.csv

<?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" encoding="UTF-8"/>


<xsl:template name="EscapeQuotes">
<xsl:param name="value"/>
<xsl:choose>
<xsl:when test="contains($value,'&quot;')">
<xsl:value-of select="substring-before($value,'&quot;')"/>
<xsl:text>&quot;&quot;</xsl:text>
<xsl:call-template name="EscapeQuotes">
<xsl:with-param name="value" select="substring-after($value,'&quot;')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$value"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>


<xsl:template name="CsvEscape">
<xsl:param name="value"/>
<xsl:choose>
<xsl:when test="contains($value,',')">
<xsl:text>&quot;</xsl:text>
<xsl:call-template name="EscapeQuotes">
<xsl:with-param name="value" select="$value"/>
</xsl:call-template>
<xsl:text>&quot;</xsl:text>
</xsl:when>
<xsl:when test="contains($value,'&#xA;')">
<xsl:text>&quot;</xsl:text>
<xsl:call-template name="EscapeQuotes">
<xsl:with-param name="value" select="$value"/>
</xsl:call-template>
<xsl:text>&quot;</xsl:text>
</xsl:when>
<xsl:when test="contains($value,'&quot;')">
<xsl:text>&quot;</xsl:text>
<xsl:call-template name="EscapeQuotes">
<xsl:with-param name="value" select="$value"/>
</xsl:call-template>
<xsl:text>&quot;</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$value"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
  

<xsl:template match="/">
<xsl:text>project,name,language,owner,state,startDate</xsl:text>
<xsl:text>&#xA;</xsl:text>
<xsl:for-each select="projects/project">
<xsl:call-template name="CsvEscape"><xsl:with-param name="value" select="normalize-space(name)"/></xsl:call-template>
<xsl:text>,</xsl:text>
<xsl:call-template name="CsvEscape"><xsl:with-param name="value" select="normalize-space(language)"/></xsl:call-template>
<xsl:text>,</xsl:text>
<xsl:call-template name="CsvEscape"><xsl:with-param name="value" select="normalize-space(owner)"/></xsl:call-template>
<xsl:text>,</xsl:text>
<xsl:call-template name="CsvEscape"><xsl:with-param name="value" select="normalize-space(state)"/></xsl:call-template>
<xsl:text>,</xsl:text>
<xsl:call-template name="CsvEscape"><xsl:with-param name="value" select="normalize-space(startDate)"/></xsl:call-template>
<xsl:text>&#xA;</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>