【问题标题】:Remove duplicate elements based on attributes and values根据属性和值删除重复元素
【发布时间】:2018-06-02 16:45:23
【问题描述】:

many questions 介绍了当您可以按某个属性或值对这些元素进行分组时如何删除重复元素,但是,在我的情况下,这些属性已经在 XSLT 中动态生成,我不想拥有为每个元素编写每个属性以用作分组键。

如何在事先不知道其属性的情况下删除重复元素?到目前为止,我已经尝试在每个元素上使用 generate-id() 并按此进行分组,但问题是 generate-id 没有为具有相同属性的元素生成相同的 ID:

<xsl:template match="root">
    <xsl:variable name="tempIds">
        <xsl:for-each select="./*>
            <xsl:copy>
                <xsl:copy-of select="@*"/>
                <xsl:attribute name="tempID">
                    <xsl:value-of select="generate-id(.)"/>
                </xsl:attribute>
                <xsl:copy-of select="node()"/>
            </xsl:copy>
        </xsl:for-each>
    </xsl:variable>
    <xsl:for-each-group select="$tempIds" group-by="@tempID">
        <xsl:sequence select="."/>
    </xsl:for-each-group>
</xsl:template>

测试数据:

<root>
    <child1>
        <etc/>
    </child1>
    <dynamicElement1 a="2" b="3"/>
    <dynamicElement2 c="3" d="4"/>
    <dynamicElement2 c="3" d="5"/>
    <dynamicElement1 a="2" b="3"/>
</root>

最终结果只是剩下的两个 dynamicElement1 元素之一:

<root>
    <child1>
        <etc/>
    </child1>
    <dynamicElement1 a="2" b="3"/>
    <dynamicElement2 c="3" d="4"/>
    <dynamicElement2 c="3" d="5"/>
</root>

【问题讨论】:

  • 您使用哪种 XSLT 处理器?您想要测试数据的哪个结果?听起来好像带有xsl:for-each-group select="*" composite="yes" group-by="@*" 的XSLT 3 可能是一个选项。但我看不出一个元素如何拥有两个 c 属性,就像你的 dynamicElement2 元素一样。
  • @MartinHonnen 抱歉,这是一个错字。我的样式表使用 XSLT 2.0,但我可以在 3.0 中创建另一个样式表,如果这样更容易的话,它会链接到另一个样式表。我使用 SAXON EE 作为我的处理器
  • “问题是 generate-id 没有为具有相同属性的元素生成相同的 ID” 嗯,你从哪里得到的概念是案例?
  • 嗯,generate-id() 的重点是生成唯一 ID,所以这不足为奇。
  • @Tomalak 我刚刚看到this 的回答。我误解并认为 generate-id 会为具有相似值和属性的节点返回相同的值,但我现在看到它也考虑了上下文。

标签: xslt xslt-2.0 saxon xslt-grouping exslt


【解决方案1】:

在 XSLT 3 中,如 https://xsltfiddle.liberty-development.net/pPqsHTi 所示,您可以使用所有属性的复合键,例如

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="3.0">

  <xsl:mode on-no-match="shallow-copy"/>

  <xsl:output indent="yes"/>

  <xsl:template match="root">
      <xsl:copy>
          <xsl:for-each-group select="*" composite="yes" group-by="@*">
              <xsl:sequence select="."/>
          </xsl:for-each-group>
      </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

请注意,从技术上讲,属性没有排序,因此通过 node-name() 或类似方法按某种属性进行分组可能更安全,就像在 https://xsltfiddle.liberty-development.net/pPqsHTi/2 中没有高阶函数的 XSLT 3 所做的那样

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:mf="http://example.com/mf"
    version="3.0">

  <xsl:mode on-no-match="shallow-copy"/>

  <xsl:output indent="yes"/>

  <xsl:function name="mf:node-sort" as="node()*">
      <xsl:param name="input-nodes" as="node()*"/>
      <xsl:perform-sort select="$input-nodes">
          <xsl:sort select="namespace-uri()"/>
          <xsl:sort select="local-name()"/>
      </xsl:perform-sort>
  </xsl:function>

  <xsl:template match="root">
      <xsl:copy>
          <xsl:for-each-group select="*" composite="yes" group-by="mf:node-sort(@*)">
              <xsl:sequence select="."/>
          </xsl:for-each-group>
      </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

或者就像你可以用 Saxon EE 做的一样

<xsl:template match="root">
    <xsl:copy>
        <xsl:for-each-group select="*" composite="yes" group-by="sort(@*, (), function($att) { namespace-uri($att), local-name($att) })">
            <xsl:sequence select="."/>
        </xsl:for-each-group>
    </xsl:copy>
</xsl:template>

【讨论】:

  • 这很好用,谢谢!就我所知,知道为什么我的generate-id 解决方案不起作用吗?
  • @CCInc, generate-id 根据节点身份为任何不同的节点生成不同的 id。所以同一个文档中的两个节点永远不会有相同的 id。
  • 我绝对不会依赖两个不同元素上的属性顺序相同。从技术上讲,使用string(node-name()) 作为排序键也是不安全的,因为它依赖于两个元素范围内的相同命名空间绑定。更安全的是使用复合排序键(namespace-uri($att), local-name($att))
  • @MichaelKay,你是对的,我已经修改了代码示例以纳入你的建议。
【解决方案2】:
<xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="root/*[@a= following-sibling::*/@a]|root/*[@c= following-sibling::*/@c and @d= following-sibling::*/@d]"/>
You may try this

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-04
    • 1970-01-01
    • 2016-11-08
    • 2016-01-22
    • 1970-01-01
    相关资源
    最近更新 更多