【问题标题】:Stuck on grouping in XSLT 2.0停留在 XSLT 2.0 中的分组
【发布时间】:2013-06-24 18:42:44
【问题描述】:

我有一个这样的输入文件

<items>
    <item>
        <id>5</id>
        <name>Harry</name>
        <age>18</age>
        <color>blue</color>
    </item>
    <item>
        <id>5</id>
        <name>Harry</name>
        <age>18</age>
        <fav_food>pizza</fav_food>
    </item>
    <item>
        <id>5</id>
        <name>Harry</name>
        <age>18</age>
        <is_single>true</is_single>
    </item>
</items>

我想对元素进行分组,使文件看起来像这样

<items>
<item>
    <id>5</id>
    <name>Harry</name>
    <age>18</age>
    <color>blue</color>
    <fav_food>pizza</fav_food>
    <is_single>true</is_single>
</item>
</items>

编辑 - 在此链接 (XSLT Consolidating data when ID is the same) 之后进行了 XSLT 转换,但这不会打印任何内容。

这是我的转换(使用 XSLT 2.0) -

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" omit-xml-declaration="yes" />
<xsl:strip-space elements="*" />  

<xsl:template match="items">
    <xsl:apply-templates select="@*|node()"/>
</xsl:template>

<xsl:template match="item">
    <xsl:apply-templates select="@*" />
    <xsl:for-each-group select="item" group-by="id">
    <item>
        <id><xsl:value-of select="current-grouping-key()"/></id>
        <xsl:apply-templates select="current-group()" />
    </item>
    </xsl:for-each-group>
</xsl:template>

<xsl:template match="@*|node()[not(self::*)]">
    <xsl:copy> 
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="*">
    <xsl:element name="{local-name()}"> 
        <xsl:apply-templates select="@*|node()"/>
    </xsl:element>
</xsl:template>

</xsl:stylesheet>

【问题讨论】:

  • 如果您使用的是 XSLT 2.0,则可以使用 for-each-group - 查看this question 的答案,这是一个类似的问题。
  • 分组在 XSLT 1.0 和 2.0 中是如此不同,你真的需要说明你使用的是哪一个。
  • 抱歉,我之前忘了说明这一点,我使用的是 XSLT 2.0,并且我用我现在拥有的 XSLT 转换更新了我的问题。
  • for-each-group 应该在items 的上下文中,而不是item - 它找不到任何东西。

标签: xml xslt xml-parsing xslt-2.0


【解决方案1】:

您遇到的主要问题是您有一个与 item 元素匹配的模板:

<xsl:template match="item">

但在其中,您有一个 xsl:for-each-group,它还查找 item 元素

<xsl:for-each-group select="item" group-by="id">

这意味着您正在寻找的 item 元素是其他 item 元素的子元素,而 XML 中没有这些元素。我认为您需要在这里将前两个模板合并为一个:

<xsl:template match="items">
    <xsl:for-each-group select="item" group-by="id">
        <item>
           ....

不清楚的一点是 item 元素中的哪些元素会被重复。会一直是身份证、姓名和年龄吗?不过,这里最好灵活一些,并假设只有 id 是通用元素。我还将假设如果两个元素重复(如 name),它将始终具有相同的值。

因此,要获取所有其他不同的非 id 元素,您可以对元素名称进行更多分组

<xsl:for-each-group select="current-group()/*[not(self::id)]" group-by="local-name()">

然后,在这个循环中你就可以输出元素了。

试试这个 XSLT

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" omit-xml-declaration="yes" />
<xsl:strip-space elements="*" />  

<xsl:template match="items">
  <xsl:for-each-group select="item" group-by="id">
    <item>
      <id><xsl:value-of select="current-grouping-key()"/></id>
      <xsl:for-each-group select="current-group()/*[not(self::id)]" group-by="local-name()">
         <xsl:apply-templates select="self::*" />
      </xsl:for-each-group>
    </item>
  </xsl:for-each-group>
</xsl:template>

<xsl:template match="@*|node()[not(self::*)]">
    <xsl:copy> 
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="*">
    <xsl:element name="{local-name()}"> 
        <xsl:apply-templates select="@*|node()"/>
    </xsl:element>
</xsl:template>

</xsl:stylesheet>

当应用于您的 XML 时,将输出以下内容

<item>
   <id>5</id>
   <name>Harry</name>
   <age>18</age>
   <color>blue</color>
   <fav_food>pizza</fav_food>
   <is_single>true</is_single>
</item>

【讨论】:

  • 这非常有效。非常感谢,我感谢详细的解释!
猜你喜欢
  • 1970-01-01
  • 2013-11-09
  • 2018-07-15
  • 2016-08-30
  • 2016-02-22
  • 1970-01-01
  • 1970-01-01
  • 2011-07-02
  • 2017-10-26
相关资源
最近更新 更多