XSLT 2 is preferred and I hope makes this easier.
Given a document similar to
<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>
I would like to have all of the unique elements based on all the attributes in bob namespace. This is a representative sample but I will have much deeper nesting so all I want it to traverse the tree for
*[@bob:*] and get the unique set of those.
The hoped for output would look like
<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>
where one element was removed for not having any @bob:* attributes and the other was removed for being a duplicate of the first based solely on the attributes.
I was trying to use a key but didn’t seem to be doing it right
<xsl:key name="BobAttributes" match="//*" use="./@bob:*" />
I also tried creating a function that concatenated all the @bob attributes but that also didn’t seem to do what I hoped.
<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>
In both cases I was using “simple” XSL to filter out the unique ones maybe I blew it here?
Variables added here to try and debug.
<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>
Looking forward to hearing what simple thing I overlooked or completely different cool approach I should have taken.
I would define the function AllBobConcat as you do, and then use this as a grouping key:
Except that AllBobConcat needs to ensure the attributes are in a canonical order, so:
Also, you shouldn’t be putting your functions in a namespace that belongs to someone else.