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>
期待听到我应该忽略的一件简单的事情,或者我应该采取的完全不同的方法。
答案 0 :(得分: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>
此外,您不应该将您的函数放在属于其他人的命名空间中。
答案 1 :(得分: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
或其他人给我们他的单线。