我正在寻找一种智能高效的XSLT,它可以将XML文档转换为CSV数据.它应该处理子节点中的所有可能元素.例如,XML看起来像这样
<?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>
Run Code Online (Sandbox Code Playgroud)
而我的输出CSV应该是这样的
Name,BillingStreet,age,location
Raagu,Hoskote,,
Rajath,BTM,25,
Sarath,Murgesh,,Bangalore
Run Code Online (Sandbox Code Playgroud)
所有行都应包含CSV中所有键的字段,即使XML确实具有值.
以下是我通过查看此处的不同示例而提出的XSLT代码.
这是我提出的XSLT
<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>
Run Code Online (Sandbox Code Playgroud)
并打印出来
Name,BillingStreet,age,location
Raagu,Hoskote
Rajath,BTM,25
Sarath,Murgesh,Bangalore
Run Code Online (Sandbox Code Playgroud)
但我希望所有行都包含第一行所有键的多次值.
你能帮我用XSLT代码帮我实现吗?
这对于两步解决方案来说如何呢?
<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="/*/*/*" use="local-name()"/>
<!-- variable containing the first occurrence of each field -->
<xsl:variable name="allFields"
select="/*/*/*[generate-id()=generate-id(key('field', local-name())[1])]" />
<xsl:template match="/">
<xsl:for-each select="$allFields">
<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:apply-templates select="*/*" />
</xsl:template>
<xsl:template match="*">
<xsl:variable name="this" select="." />
<xsl:for-each select="$allFields">
<xsl:value-of select="$this/*[local-name() = local-name(current())]" />
<xsl:if test="position() < last()">
<xsl:value-of select="$delimiter" />
</xsl:if>
</xsl:for-each>
<xsl:text> </xsl:text>
</xsl:template>
</xsl:stylesheet>
Run Code Online (Sandbox Code Playgroud)
这里的诀窍是,allFields
变量将包含每个名称的一个元素,所以它的这个,我们遍历每一行,不能简单地认为实际存在该行的元素节点列表.因为你说你想在任意命名空间等中支持XML.我使用了模式/*/*/*
而不是硬编码任何特定的元素名称(/*/*/*
简单地匹配任何元素是文档元素的孙子,不管元素名称如何),以及我正在使用local-name()
而不是name()
忽略任何名称空间前缀(它会对待<sObject>
,<sObject xmlns="foo">
并且<f:sObject xmlns:f="foo">
完全相同).