Sebastien
Sebastien

Reputation: 2714

Sequence concatenation or union when using except operator

I am trying to understand the difference (or lack thereof) between using sequence concatenation or sequence union in this case, so as an example :

Input XML:

<?xml version="1.0" encoding="utf-8" ?>
<document>
  <someElement a="1" b="2" c="3" d="4"/>
</document>

XSLT:

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

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

  <xsl:output method="xml" indent="yes"/>

  <xsl:template match="someElement">
    
    <xsl:copy>
      <xsl:copy-of select="@* except (@c, @d)"/>
    </xsl:copy>
    
    <xsl:copy>
      <xsl:copy-of select="@* except (@c | @d)"/>
    </xsl:copy>
    
  </xsl:template>
  
</xsl:stylesheet>

Output:

<?xml version="1.0" encoding="UTF-8"?>
<document>
   <someElement a="1" b="2"/>
   <someElement a="1" b="2"/>
</document>

So I was using xsl:copy with the except operator, as explained on page 261 or Dr. Kay's XSLT and XPath 4th ed. book. There the comma is used to build the sequence.
I also tried with the uninon operator and got the same result. On page 537, the operators are defined as :

So when using either, is there a difference?

Upvotes: 0

Views: 164

Answers (3)

michael.hor257k
michael.hor257k

Reputation: 117140

When you construct a sequence using the comma operator, the operands are concatenated in the order that you list them, and the sequence may contain duplicate items.

OTOH, when you use the union operator, items are listed in document order and duplicate nodes are eliminated.

Here is a more illustrative example:

XML

<root>
    <alpha/>
    <bravo/>
    <charlie/>
</root>

XSLT 2.0

<xsl:stylesheet version="2.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:template match="/root">
    <xsl:copy>
        <comma>
            <xsl:copy-of select="bravo, alpha, charlie, bravo"/>
        </comma>
        <union>
            <xsl:copy-of select="bravo | alpha | charlie | bravo"/>
        </union>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

Result

<?xml version="1.0" encoding="UTF-8"?>
<root>
   <comma>
      <bravo/>
      <alpha/>
      <charlie/>
      <bravo/>
   </comma>
   <union>
      <alpha/>
      <bravo/>
      <charlie/>
   </union>
</root>

Added:

In my answer above I chose to ignore the part about the except operator and concentrate on the differences between the comma and union operators when constructing a sequence.

As the other answers pointed out, when the except operator is added to the constructing expression, these differences are effectively wiped out.

To demonstrate this in a rather extreme way, the instruction:

        <except>
            <xsl:copy-of select="(bravo, alpha, charlie, bravo) except()"/>
        </except>
    
                

when used in the above example, will produce:

   <except>
      <alpha/>
      <bravo/>
      <charlie/>
   </except>

Upvotes: 2

Michael Kay
Michael Kay

Reputation: 163595

The expression X except Y selects every node that is in X and is not in Y.

The expressions (P|Q) and (P,Q) deliver the same nodes, though the results may be in a different order and the second form may include duplicates. But since both forms include the same nodes, they are interchangeable when used on the right-hand side of "except".

Upvotes: 1

Martin Honnen
Martin Honnen

Reputation: 167716

For the except operator, which is a "set" operator, like union, | and like intersect, it makes no difference as https://www.w3.org/TR/xpath-31/#combining_seq states: "All these operators eliminate duplicate nodes from their result sequences based on node identity. The resulting sequence is returned in document order".

Upvotes: 2

Related Questions