通过xslt转换xml到csv

时间:2014-01-07 22:23:34

标签: java xml xslt

我知道有很多问题,但我仍然坚持使用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>&#xa;</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>&#xa;</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>

我将非常感谢任何暗示。提前谢谢。

1 个答案:

答案 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>&#xa;</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>&#xa;</xsl:text>
                </xsl:if>
             </xsl:for-each>
        </xsl:for-each>
     </xsl:template>
 </xsl:stylesheet>

这应该适用于您的XML示例。另请注意,没有提到 sObject (假设是根元素的子元素对应于记录,而叶子元素对应于字段)

请注意,XSLT使用一种名为Muenchian Grouping的技术来确定不同的字段名称。阅读并了解它。