When you create an xsl:variable
without using @select
and do not specify the type with the @as
, it will create the variable as a temporary tree.
You want to create a sequence of nodes, so that when they are compared in the except
operator, they are "seen" as the same nodes. You can do this by specifying as="node()*"
for the xsl:variable
and by using xsl:sequence
instead of xsl:copy-of
:
<xsl:variable name="badValues" as="node()*">
<xsl:for-each select="$root/A[not(VALUE)]">
<xsl:choose>
<xsl:when test="count($root/A[DOMAIN=current()/DOMAIN
and VARIABLE=current()/VARIABLE])=1">
<xsl:sequence select="."/>
</xsl:when>
</xsl:choose>
</xsl:for-each>
</xsl:variable>
Alternatively, if you were to use a @select
and eliminate the xsl:for-each
it would also work. As Martin Honnen suggested, you could use an xsl:key
and select the values like this:
<xsl:key name="by-dom-and-var" match="A" use="concat(DOMAIN, '|', VARIABLE)"/>
Then change your badValues to this:
<xsl:variable name="badValues"
select="$root/A[not(VALUE)]
[count(key('by-dom-and-var',
concat(DOMAIN, '|', VARIABLE))/VARIABLE) = 1]"/>>
You can see the difference in the identity of the nodes by using the generate-id()
function as you iterate over the items by executing this stylesheet:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:template match="/">
<xsl:variable name="root" select="*" as="item()*"/>
<xsl:variable name="originalBadValues">
<xsl:for-each select="$root/A[not(VALUE)]">
<xsl:choose>
<xsl:when test="count($root/A[DOMAIN=current()/DOMAIN
and VARIABLE=current()/VARIABLE])=1">
<xsl:copy-of select="."/>
</xsl:when>
</xsl:choose>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="badValues" as="node()*">
<xsl:for-each select="$root/A[not(VALUE)]">
<xsl:choose>
<xsl:when test="count($root/A[DOMAIN=current()/DOMAIN
and VARIABLE=current()/VARIABLE])=1">
<xsl:sequence select="."/>
</xsl:when>
</xsl:choose>
</xsl:for-each>
</xsl:variable>
<!--These are the generated ID values of all the A elements-->
<rootA>
<xsl:value-of select="$root/A/generate-id()"
separator=", "/>
</rootA>
<!--These are the generated ID values for
the original $badValues/A -->
<originalBadValues>
<xsl:value-of select="$originalBadValues/A/generate-id()"
separator=", " />
</originalBadValues>
<!--These are the generated ID values for
the correct selection of $badValues-->
<badValues>
<xsl:value-of select="$badValues/generate-id()"
separator=", " />
</badValues>
<!--The generated ID values for the result of
the except operator filter-->
<final>
<xsl:value-of select="($root/A except $badValues)/generate-id()"
separator=", "/>
</final>
</xsl:template>
</xsl:stylesheet>
Executed against this XML file:
<doc>
<A>
<VALUE>skip me</VALUE>
<DOMAIN>a</DOMAIN>
<VARIABLE>a</VARIABLE>
</A>
<A>
<DOMAIN>a</DOMAIN>
<VARIABLE>a</VARIABLE>
</A>
<A>
<DOMAIN>b</DOMAIN>
<VARIABLE>b</VARIABLE>
</A>
<A>
<DOMAIN>c</DOMAIN>
<VARIABLE>c</VARIABLE>
</A>
<A>
<DOMAIN>a</DOMAIN>
<VARIABLE>a</VARIABLE>
</A>
</doc>
It produces the following output:
<rootA>d1e3, d1e15, d1e24, d1e33, d1e42</rootA>
<originalBadValues>d2e1, d2e9</originalBadValues>
<badValues>d1e24, d1e33</badValues>
<final>d1e3, d1e15, d1e42</final>