将子项提取为行

时间:2017-04-28 08:49:11

标签: csv xslt

我有一个XML文件,我试图将其转换为csv。 我已经在过去做过类似的事情,但这次的问题是我的CSV列包含XML中的父项。这导致了问题。

给出以下示例XML结构:

<...>
    <thing name="Type1">
        <...>
            <category name="cat1">
                <value>12.65456</value>
            </category>
            <category name="cat2">
                <value>4.56785</value>
            </category>
            <category name="cat3">
                <value>1.3658</value>
            </category>                        
        </...>
    </thing>
    <thing name="Type2">
        <...>
            <category name="cat1">
                <value>xx.xxxxx</value>
            </category>
            <category name="cat2">
                <value>xx.xxxxx</value>
            </category>
            <category name="cat3">
                <value>xx.xxxxx</value>
            </category>    
            <category name="cat4">
                <value>xx.xxxx</value>
            </category>                        
        </...>
    </thing> 
    <thing name="Type3">
        <...>
            <category name="cat1">
                <value>xx.xxxxx</value>
            </category>
            <category name="cat2">
                <value>xx.xxxxx</value>
            </category>   
            <category name="cat4">
                <value>xx.xxxx</value>
            </category>                        
        </...>
    </thing>    
</...>

请注意在XML文档中:

  • 所有类型都不包含每个类别
  • 所有类型按相同顺序列出类别

我想获得以下输出csv文件

        Type1       Type2       Type3   ....
cat1    12.65456    xx.xxxx     xx.xxxx
cat2    4.56785     xx.xxxx     xx.xxxx
cat3    1.3658      xx.xxxx     
cat4                xx.xxxx     xx.xxxx
....    .....       .......     .......

(请注意,为了便于阅读,我添加了标签,但我在转换中使用了逗号。这不重要。)

起初,我试图仔细检查每件事情:

  <xsl:template match=".../thing">
      <xsl:for-each select="category">

        <xsl:choose>
          <xsl:when test="@name='cat1'">
              ...
          </xsl:when>
          <xsl:when test="@name='cat2'">
              ...
          </xsl:when>
          <xsl:otherwise>
          </xsl:otherwise>
        </xsl:choose>

      </xsl:for-each>
  </xsl:template>

但这里的问题是输出的行会被扰乱:

12.65456 xx.xxxx xx.xxxx 4.56785 xx.xxxx xx.xxxx ....

我也尝试直接点击类别

<xsl:template match=".../thing/category[cat1]">
  ....
</xsl:template>
<xsl:template match=".../thing/category[cat2]">
  ....
</xsl:template>
....

但由于XSLT通过树的方式,我也无法确保输出的顺序是正确的。

最后,我一直在研究如何拯救&#34;成长&#34;结果变成了变量,但事实证明在XSLT中不能覆盖变量。

所以我的问题是:有没有办法在树中运行以便获得所需的输出?

任何领导都会非常感激:)。

谢谢, 于连

2 个答案:

答案 0 :(得分:2)

首先,让我们有一个可行的输入示例:

<强> XML

<root>
  <thing name="Type1">
    <wrapper>
      <category name="cat1">
        <value>1.10</value>
      </category>
      <category name="cat2">
        <value>1.20</value>
      </category>
      <category name="cat3">
        <value>1.30</value>
      </category>
    </wrapper>
  </thing>
  <thing name="Type2">
    <wrapper>
      <category name="cat1">
        <value>2.10</value>
      </category>
      <category name="cat2">
        <value>2.20</value>
      </category>
      <category name="cat3">
        <value>2.30</value>
      </category>
      <category name="cat4">
        <value>2.40</value>
      </category>
    </wrapper>
  </thing>
  <thing name="Type3">
    <wrapper>
      <category name="cat1">
        <value>3.10</value>
      </category>
      <category name="cat2">
        <value>3.20</value>
      </category>
      <category name="cat4">
        <value>3.40</value>
      </category>
    </wrapper>
  </thing>
</root>

现在,简而言之,您希望为category/@name的每个不同值和每个thing的数据单元创建一行。

XSLT 2.0

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

<xsl:template match="/root">
    <xsl:variable name="columns" select="thing"/>
    <!-- header -->
    <xsl:text>Category&#9;</xsl:text>
    <xsl:value-of select="$columns/@name" separator="&#9;"/>
    <xsl:text>&#10;</xsl:text>
     <!-- data -->
   <xsl:for-each select="distinct-values(thing/wrapper/category/@name)">
        <xsl:variable name="cat" select="."/>
        <xsl:value-of select="$cat"/>
        <xsl:text>&#9;</xsl:text>
        <xsl:for-each select="$columns">
            <xsl:value-of select="wrapper/category[@name=$cat]/value" />
            <xsl:if test="position()!=last()">
                <xsl:text>&#9;</xsl:text>
            </xsl:if>
        </xsl:for-each>
        <xsl:text>&#10;</xsl:text>
    </xsl:for-each>
</xsl:template>

</xsl:stylesheet>

<强>结果

Category  Type1     Type2     Type3
cat1      1.10      2.10      3.10
cat2      1.20      2.20      3.20
cat3      1.30      2.30      
cat4                2.40      3.40

使用获取每个数据单元格的值可以提高效率。

答案 1 :(得分:1)

您可以通过以下方式执行此操作:

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

  <xsl:template match="root">
    <xsl:variable name="thisRoot" select="."/>
    <!-- Title line -->
    <!-- Empty title for categories -->
    <xsl:text>&#x9;</xsl:text>
    <!-- Type names -->
    <xsl:variable name="types" select="thing/@name"/>
    <xsl:value-of select="string-join($types, '&#x9;')"/>
    <xsl:text>&#xA;</xsl:text>
    <!-- A line for each category -->
    <xsl:variable name="categories" select="distinct-values(//category/@name)"/>
    <xsl:for-each select="$categories">
      <!-- The current category -->
      <xsl:variable name="thisCateg" select="."/>
      <xsl:value-of select="."/>
      <xsl:text>&#x9;</xsl:text>
      <!-- Loop for each type in this row -->
      <xsl:for-each select="$types">
        <xsl:variable name="thisType" select="."/>
        <xsl:value-of select=
          "$thisRoot/thing[@name = $thisType]//category[@name = $thisCateg]/value"/>
        <xsl:if test="position() &lt; last()">
          <xsl:text>&#x9;</xsl:text>
        </xsl:if>
      </xsl:for-each>
      <xsl:text>&#xA;</xsl:text>
    </xsl:for-each>
  </xsl:template>
</xsl:transform>

请注意,需要将当前对象(.)存储在变量中 供将来参考,因为当前对象的当前分配 不同地方的变化。

从输入文件中读取类型和类别,无需硬编码 它们在XSLT脚本中。

对于您的数据(略有变化),我得到以下结果:

    Type1   Type2   Type3
cat1    12.65456    xx.xxxx1    yy.xxxx1
cat2    4.56785 xx.xxxx2    yy.xxxx2
cat3    1.3658  xx.xxxx3    yy.xxxx3
cat4        xx.xxx4 xx.xxx4