我知道有很多问题,但我仍然坚持使用xslt将xml转换为csv的问题。我使用了一个示例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>
此代码适用于此:
<?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>
我在SO上找到的例子,但是有人能给我一个关于如何处理xml的暗示吗?说:
<?xml version="1.0" encoding="ISO-8859-1"?>
<sObjects>
<sObject>
<Person>
<Name>Raagu</Name>
<BillingStreet>Hoskote</BillingStreet>
</Person>
<Country>Germany</Country>
</sObject>
<sObjects>
我将非常感谢任何暗示。提前谢谢。
答案 0 :(得分:0)
如果您可以假设左侧节点始终与CSV输出中的字段对应,则应考虑重新定义字段键,如此
<xsl:key name="field" match="*[not(*)]" use="local-name()"/>
(我在这里使用“local-name()”,以便它可以处理涉及命名空间的XML)
然后,定义一个变量以获得不同的字段名称
<xsl:variable
name="fields"
select="//*[not(*)][generate-id()=generate-id(key('field', local-name())[1])]" />
这里//*
将在XML中的任何深度查找元素。然后,您可以轻松地迭代此变量以输出当前的标题行。到目前为止一切都很好。
然而,尽管你说当前的XSLT运行良好,但事实并非如此。因为这一行:
<xsl:for-each select="$property/*">
这假设每个 sObject 具有相同数量的子元素(即它包含所有字段,并且顺序正确),而在XML样本中则不是这样。
首先,我会将属性变量重新定义为当前元素下任何深度的所有“叶子”元素。
<xsl:variable name="properties" select=".//*[not(*)]" />
然后,您需要遍历字段变量,而不是属性元素
<xsl:for-each select="$fields">
然后使用属性元素查找其中与当前字段名称匹配的元素,如下所示
<xsl:value-of select="$properties[local-name()=local-name(current())]"/>
尝试以下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="*[not(*)]" use="local-name()"/>
<xsl:variable name="fields" select="//*[not(*)][generate-id()=generate-id(key('field', local-name())[1])]" />
<xsl:template match="/">
<xsl:for-each select="$fields">
<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:for-each select="/*/*">
<xsl:variable name="properties" select=".//*[not(*)]" />
<xsl:for-each select="$fields">
<xsl:value-of select="$properties[local-name()=local-name(current())]"/>
<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>
这应该适用于您的XML示例。另请注意,没有提到 sObject (假设是根元素的子元素对应于记录,而叶子元素对应于字段)
请注意,XSLT使用一种名为Muenchian Grouping的技术来确定不同的字段名称。阅读并了解它。