【问题标题】:How to output duplicate elements using XSLT?如何使用 XSLT 输出重复元素?
【发布时间】:2011-05-09 12:47:21
【问题描述】:

我有一个看起来像这样的 xml -

<Root>
  <Fields>
    <Field name="abc" displayName="aaa" />
    <Field name="pqr" displayName="ppp" />
    <Field name="abc" displayName="aaa" />
    <Field name="xyz" displayName="zzz" />
  </Fields>
</Root>

我希望输出仅包含那些具有重复 name-displayName 组合的元素,如果有的话 -

<Root>
      <Fields>
        <Field name="abc" displayName="aaa" />
        <Field name="abc" displayName="aaa" />
      </Fields>
</Root>

如何使用 XSLT 做到这一点?

【问题讨论】:

  • 好问题,+1。请参阅我的答案以获得简短、简单且高效的 XSLT 1.0 解决方案。
  • 还添加了 XSLT 2.0 解决方案。

标签: xml xslt


【解决方案1】:

这种转变

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

 <xsl:key name="kFieldByName" match="Field"
  use="concat(@name, '+', @displayName)"/>

 <xsl:template match=
  "Field[generate-id()
        =
         generate-id(key('kFieldByName',
                     concat(@name, '+', @displayName)
                     )[2])
        ]
  ">
     <xsl:copy-of select=
     "key('kFieldByName',concat(@name, '+', @displayName))"/>
 </xsl:template>
</xsl:stylesheet>

应用于提供的 XML 文档时

<Root>
    <Fields>
        <Field name="abc" displayName="aaa" />
        <Field name="pqr" displayName="ppp" />
        <Field name="abc" displayName="aaa" />
        <Field name="xyz" displayName="zzz" />
    </Fields>
</Root>

产生想要的结果

<Field name="abc" displayName="aaa"/>
<Field name="abc" displayName="aaa"/>

解释

  1. Muenchian grouping 使用复合键(在namedisplayName 属性上)。

  2. 代码中唯一的模板匹配其对应组中的第二个Field 元素。然后,在模板的主体内,输出整个组。

  3. Muenchian 分组是在 XSLT 1.0 中进行分组的有效方法。密钥用于提高效率。

  4. 另请参阅我对 this question 的回复。

二。 XSLT 2.0 解决方案

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

 <xsl:template match="/">
     <xsl:for-each-group select="/*/*/Field"
          group-by="concat(@name, '+', @displayName)">
       <xsl:sequence select="current-group()[current-group()[2]]"/>
   </xsl:for-each-group>
 </xsl:template>
</xsl:stylesheet>

当对提供的 XML 文档(如上所示)应用此转换时,再次生成所需的正确结果

<Field name="abc" displayName="aaa"/>
<Field name="abc" displayName="aaa"/>

解释

  1. 使用&lt;xsl:for-each-group&gt;

  2. current-group()函数的使用。

【讨论】:

    【解决方案2】:

    要查找重复项,您需要迭代Field 元素,并为每个元素在整个文档中查找具有匹配namedisplayName 属性值的Field 元素集。如果集合有超过 1 个元素,则将该元素添加到输出中。

    这是实现此目的的模板示例:

    <xsl:template match="Field">
        <xsl:variable name="fieldName" select="@name" />
        <xsl:variable name="fieldDisplayName" select="@displayName" />
        <xsl:if test="count(//Field[@name=$fieldName and @displayName=$fieldDisplayName]) > 1">
            <xsl:copy-of select="."/>
        </xsl:if>
    </xsl:template>
    

    对示例数据执行此模板(包装在适当的 XSLT 文件中)会产生以下输出:

    <?xml version="1.0" encoding="utf-8"?>
    <Root>
      <Fields>
        <Field name="abc" displayName="aaa" />
        <Field name="abc" displayName="aaa" />
      </Fields>
    </Root>
    

    【讨论】:

    • @Jeff Yates:这是一种可能的解决方案,但是它的效率是 O(N^2),并且对于具有大量 Field 元素的 XML 文档来说太慢了。请参阅我的答案以获得有效的解决方案。
    • @Dimitre:做不必要的努力似乎很愚蠢。没有理由相信真正的 XML 会很大并且没有分析信息。在分析开始之前,我会在任何一天快速写完快速运行。
    • @Jeff Yates:可以而且应该使用已知最有效的解决方案。因为人们不这样认为,我们每天都会遇到关于运行 40 分钟的转换的问题,而当使用 Muenchian 分组进行重构时,只需 2 秒。我们不应该传播糟糕和幼稚的算法。
    • @Dimitre:你是对的,尽管在优化前还应该考虑实施和维护的成本。
    • 虽然在许多 XSLT 处理器上效率可能为 O(N^2),但在优化处理器上可能要好得多 - 在 Saxon-EE 上试试。但是,我同意最好不要过于依赖优化器 - 使用 xsl:for-each-group。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-11-11
    • 1970-01-01
    • 2013-09-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多