1

XSLT 2 是首选,我希望使这更容易。

给定一个类似于

<doc xmlns:bob="bob.com">
    <element bob:name="fred" bob:occupation="Dr">Stuff</element>
    <element bob:name="Bill" bob:occupation="Dr" bob:birthMonth="Jan"/>
    <element>Kill Me</element>
    <element bob:name="fred" bob:occupation="Dr">different Stuff</element>
</doc>

我想拥有基于 bob 命名空间中所有属性的所有唯一元素。这是一个有代表性的样本,但我会有更深的嵌套,所以我希望它遍历树

*[@bob:*] and get the unique set of those.

希望的输出看起来像

<doc xmlns:bob="bob.com">
    <element bob:name="fred" bob:occupation="Dr">Stuff</element>
    <element bob:name="Bill" bob:occupation="Dr" bob:birthMonth="Jan"/>
</doc>

其中一个元素因没有任何 @bob:* 属性而被删除,另一个元素因仅基于属性而与第一个元素重复而被删除。

我试图使用钥匙,但似乎做得不对

<xsl:key name="BobAttributes" match="//*" use="./@bob:*" />

我还尝试创建一个连接所有 @bob 属性的函数,但这似乎也没有达到我的预期。

<xsl:key name="BobAttributes" match="//*" use="functx:AllBobConcat(.)" />

 <xsl:function name="functx:AllBobConcat" as="xs:string*" 
    xmlns:functx="http://www.functx.com" >
    <xsl:param name="nodes" as="node()*"/> 

    <xsl:for-each select="$nodes/@bob:*">
        <xsl:value-of select="local-name(.)"/>
        <xsl:value-of select="."/>
    </xsl:for-each>
</xsl:function>

在这两种情况下,我都使用“简单”XSL 来过滤掉唯一的 XSL,也许我在这里搞砸了?此处添加的变量用于尝试和调试。

  <xsl:template match="*[@ism:*]" priority="100">
        <xsl:variable name="concat">
            <xsl:value-of select="functx:AllBobConcat(.)"/>
        </xsl:variable>

        <xsl:variable name="myID">
            <xsl:value-of select="generate-id() "/>
        </xsl:variable>

        <xsl:variable name="Keylookup">
            <xsl:value-of select="key('BobAttributes', $concat)"/>
        </xsl:variable>
        <xsl:value-of select="concat($concat, $Keylookup, $myID)"/>
        <xsl:if test="generate-id() = generate-id(key('BobAttributes', $concat)[1])">

            <xsl:apply-templates select="." mode="copy"/>
        </xsl:if>
    </xsl:template>

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

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

期待听到我忽略了什么简单的事情,或者我应该采取完全不同的酷方法。

4

2 回答 2

2

我会像您一样定义函数 AllBobConcat,然后将其用作分组键:

<xsl:for-each-group select="element" group-by="f:AllBobConcat(.)">
  <xsl:if test="current-grouping-key() != ''">
    <xsl:copy-of select="current-group()[1]"/>
  </xsl:if>
</xsl:for-each-group>

除了 AllBobConcat 需要确保属性处于规范顺序之外,因此:

<xsl:function name="f:AllBobConcat" as="xs:string">
    <xsl:param name="node" as="element(element)"/> 
    <xsl:value-of>
      <xsl:for-each select="$node/@bob:*">
        <xsl:sort select="local-name()"/>
        <xsl:value-of select="local-name(.)"/>
        <xsl:value-of select="'='"/>
        <xsl:value-of select="."/>
        <xsl:value-of select="' '"/>
      </xsl:for-each>
    </xsl:value-of>
</xsl:function>

此外,您不应该将您的函数放在属于其他人的命名空间中。

于 2013-01-27T09:18:09.343 回答
1

几乎可以肯定有更好的方法,但是 FWIW,这是一个蛮力、程序性惊人、效率极低的解决方案:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
    version="1.0"
    xmlns:bob="bob.com"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    >

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

  <xsl:template match="element">
    <xsl:variable name="this" select="."/>
    <xsl:variable name="match">
      <xsl:for-each select="preceding::element">
        <xsl:variable name="diff">
          <xsl:variable name="that" select="."/>
          <xsl:for-each select="$this/@bob:*">
            <xsl:variable name="att-name" select="name()"/>
            <xsl:variable name="att-val" select="."/>
            <xsl:for-each select="$that/@bob:*[name()=$att-name]">
              <xsl:if test=". != $att-val">
                DIFF
              </xsl:if>
            </xsl:for-each>
          </xsl:for-each>
        </xsl:variable>
        <xsl:if test="$diff = ''">MATCH</xsl:if>
      </xsl:for-each>
    </xsl:variable>

    <xsl:if test="$match = ''">
      <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
      </xsl:copy>
    </xsl:if>
  </xsl:template>

</xsl:stylesheet>

对于每个元素,循环遍历所有前面的元素。对于每个属性,检查值是否相等,如果不相等,则引发 DIFF 标志。任何没有引发 DIFF 标志的前面元素都会引发 MATCH 标志。然后仅在未引发 MATCH 标志时才通过元素。

感觉就像我在用汇编语言编程。现在我们将坐下来等待迈克尔给我们他的单线使用deep-equal或类似的东西。

于 2013-01-27T07:25:51.417 回答