将XML转换为CSV,新行上的每个条目

时间:2015-02-13 15:57:39

标签: xml csv xslt

我正在使用XSLT将XML转换为CSV,但是在将XSL应用于以下XML时

<?xml version="1.0" encoding="UTF-8"?>
<Report>
<Dates>
  <Current>02/02/2014</Current>
  <Previous>02/02/2013</Previous>
</Dates>
<Entry>
  <Category>1</Category>
  <Data1>10.00</Data1>
  <Data2>20.00</Data2>
</Entry>
<Entry>
  <Category>2</Category>
  <Data1>30.00</Data1>
  <Data2>40.00</Data2>
</Entry>
<Entry>
  <Category>3</Category>
  <Data1>50.00</Data1>
  <Data2>60.00</Data2>
</Entry>
<Entry>
  <Category>4</Category>
  <Data1>70.00</Data1>
  <Data2>80.00</Data2>
</Entry>
<Totals>
  <TCurrent>160.00</TCurrent>
  <TPrevious>200.00</TPrevious>
</Totals>
</Report>

我正在使用的XSL文件如下:

    <xsl:for-each select="Report/Dates/child::*">   
    <xsl:if test="position() != last()"><xsl:value-of select="."/>,</xsl:if>    
    <xsl:if test="position()  = last()"><xsl:value-of select="."/><xsl:text>&#xD;</xsl:text></xsl:if>
    </xsl:for-each>                         

    Number, Data1 , Data2   
    <xsl:for-each select="Report/Entry/child::*">   
    <xsl:if test="position() != last()"><xsl:value-of select="."/>,</xsl:if>
    <xsl:if test="position()  = last()"><xsl:value-of select="."/><xsl:text>&#xD;</xsl:text></xsl:if>   
    </xsl:for-each>                         

    <xsl:for-each select="Report/Totals/child::*">
    <xsl:if test="position()  = 1">Totals,</xsl:if>
    <xsl:if test="position() != last()"><xsl:value-of select="."/>,</xsl:if>
    <xsl:if test="position()  = last()"><xsl:value-of select="."/><xsl:text>&#xD;</xsl:text></xsl:if>
    </xsl:for-each>                                     
</xsl:template>    
</xsl:stylesheet>

我得到一条包含所有条目的长行,例如

02/02/2014, 02/02/2013
1, 10.00, 20.00, 2, 30.00, 40.00, 3, 50.00, 60.00, 4, 70.00, 80.00
Totals, 160.00, 200.00

问题:如何在新行上获取每个条目,我需要像

这样的结果
02/02/2014, 02/02/2013 
1, 10.00, 20.00 
2, 30.00, 40.00 
3, 50.00, 60.00 
4, 70.00, 80.00 
Totals, 160.00, 200.00

2 个答案:

答案 0 :(得分:0)

<xsl:for-each select="Report/Entry/child::*">  

所以所有的所有孩子都有条目项目

您希望每个条目返回一个回车符:

<xsl:for-each select="Report/Entry">   
  <xsl:for-each select="*">   
    <xsl:if test="position() != last()"><xsl:value-of select="."/>,</xsl:if>
    <xsl:if test="position()  = last()"><xsl:value-of select="."/><xsl:text>&#xD;</xsl:text></xsl:if>   
  </xsl:for-each>      
</xsl:for-each>  

这样你就可以将这些集合“分解”为条目,然后在每个

之后放置CR

NB child ::是默认轴,即Report/Totals/child::*&lt; =&gt; Report/Totals/*

答案 1 :(得分:0)

与所有编程语言一样,有一些编程风格可行,但被认为是解决问题的不良或非常规方法。只有匹配/并在代码中大量使用for-each循环就是这种情况。

因此,我认为,对你的问题所接受的答案只会在没有真正改善的情况下触及问题的表面。如果您可以使用XSLT 2.0,我建议采用一种更简单的方法。

XSLT样式表(2.0)

使用Report匹配同一模板中|的所有子元素。然后,string-join()所有子元素,将内容与,分开。使用XPath 2.0条件表达式来决定是否输出回车。

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
    <xsl:output method="text"  encoding="UTF-8" indent="yes" />

    <xsl:strip-space elements="*"/>

    <xsl:template match="Dates|Entry|Totals">
        <xsl:if test="self::Totals">
            <xsl:text>Totals, </xsl:text>
        </xsl:if>
        <xsl:value-of select="string-join(*,', ')"/>
        <xsl:value-of select="if (position() != last()) then '&#xD;' else ''"/>
    </xsl:template>

</xsl:transform>

文字输出

如您所见,输出正是您所要求的。

02/02/2014, 02/02/2013
1, 10.00, 20.00
2, 30.00, 40.00
3, 50.00, 60.00
4, 70.00, 80.00
Totals, 160.00, 200.00

另外,也许更重要的是,为什么原始代码不起作用?这就是它的样子:

<xsl:for-each select="Report/Entry/child::*">   
  <xsl:if test="position() != last()"><xsl:value-of select="."/>,</xsl:if>
  <xsl:if test="position()  = last()"><xsl:value-of select="."/><xsl:text>&#xD;</xsl:text></xsl:if>   
</xsl:for-each>

问题是for-each遍历一系列节点,在这种情况下是Entry个元素的所有子节点。在这个新序列中,只有一个最后一个元素,而不是几个,正如您所期望的那样。所以,这if

<xsl:if test="position()  = last()"><xsl:value-of select="."/><xsl:text>&#xD;</xsl:text></xsl:if>
对于最后一个Entry元素的最后一个子元素,

仅触发一次。这就是为什么所有内容都在同一行,并且行尾只有一个&#xD;

  

我认为Report/Entry/child::*<xsl:for-each select="Report/Entry"> <xsl:for-each select="*">

相同

是的,它们针对相同的节点,但position()的行为不同。