Set Operations

XML Path Language (XPath) supports the set operation |.

Union (|) Operator

The |, or union, operator returns the union of its two operands, which must be node-sets. For example, //author | //publisher returns a node-set that combines all the //author nodes and all the //publisher nodes. Multiple union operators can be chained together to combine multiple node-sets. For example, //author | //publisher | //editor | //book-seller returns a node-set containing all //author, //publisher, //editor, and //book-seller elements. The union operator preserves document order and does not return duplicates.

Examples

Expression Refers to

first-name | last-name

A node set containing <first-name> and <last-name> elements in the current context.

(bookstore/book | bookstore/magazine)

A node set containing <book> or <magazine> elements inside a <bookstore> element.

book | book/author

A node set containing all <book> elements and all <author> elements within <book> elements.

(book | magazine)/price

The node set containing all <price> elements of either <book> or <magazine> elements.

Example

The following example illustrates the effect of the union operator.

XML File (test.xml)

<?xml version="1.0"?>
<test>
    <x a="1">
      <x a="2" b="B">
        <x>
          <y>y31</y>
          <y>y32</y>
        </x>
      </x>
    </x>
</test>

XSLT File (test.xsl)

The following XSLT style sheet selects all the <x> elements whose a attribute is equal to 2, plus those <x> elements that have no attributes.

<?xml version='1.0'?>
<xsl:stylesheet version="1.0"
      xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>

   <!-- Suppress text nodes not covered in subsequent template rule. -->
   <xsl:template match="text()"/>

  <!-- Handles a generic element node. -->
   <xsl:template match="*">
      <xsl:element name="{name()}">
         <xsl:apply-templates select="*|@*" />
         <xsl:if test="text()">
            <xsl:value-of select="."/>
         </xsl:if>
      </xsl:element>
   </xsl:template>

   <!-- Handles a generic attribute node. -->
   <xsl:template match="@*">
      <xsl:attribute name="{name()}">
         <xsl:value-of select="."/>
      </xsl:attribute>
   </xsl:template>

   <xsl:template match="/test">
      <xsl:apply-templates select="//x[@a=2] | //x[not(@*)]"/>
   </xsl:template>

</xsl:stylesheet>

The transformation yields the following result:

<x a="2" b="B">
   <x>
      <y>31</y>
      <y>y32</y>
   </x>
</x>
<x>
   <y>y31</y>
   <y>y32</y>
</x>

Precedence

Precedence order (from highest precedence to lowest) between Boolean and comparison operators is shown in the following table.


Precedence Operators Description

1

( )

Grouping

2

[ ]

Filters

3

/

//

Path operations

4

&lt;

&lt;=

&gt;

&gt;=

Comparisons

5

=

!=

Comparisons

6

|

Union

7

not()

Boolean not

8

and

Boolean and

9

or

Boolean or

Example

The following example illustrates the effect of the operator precedence listed above.

XML File (test.xml)

<?xml version="1.0"?>
<test>

    <x a="1">
      <x a="2" b="B">
        <x>
          <y>y31</y>
          <y>y32</y>
        </x>
      </x>
    </x>

    <x a="1">
      <x a="2">
        <y>y21</y>
        <y>y22</y>
      </x>
    </x>

    <x a="1">
      <y>y11</y>
      <y>y12</y>
    </x>

    <x>
      <y>y03</y>
      <y>y04</y>
    </x>

</test>

Basic XSLT File (test.xsl)

We will use this basic XSLT file as a starting point for the series of illustrations that follow.

<?xml version='1.0'?>
<xsl:stylesheet version="1.0"
      xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>

   <!-- Suppress text nodes not covered in subsequent template rule. -->
   <xsl:template match="text()"/>

  <!-- Handles a generic element node. -->
   <xsl:template match="*">
      <xsl:element name="{name()}">
         <xsl:apply-templates select="*|@*" />
         <xsl:if test="text()">
            <xsl:value-of select="."/>
         </xsl:if>
      </xsl:element>
   </xsl:template>

   <!-- Handles a generic attribute node. -->
   <xsl:template match="@*">
      <xsl:attribute name="{name()}">
         <xsl:value-of select="."/>
      </xsl:attribute>
   </xsl:template>

</xsl:stylesheet>

Case 0. Test run

You can add the following template-rule to the XSLT style sheet.

<xsl:template match="/test">
      <xsl:apply-templates select="*|@*/>
   </xsl:template>

This will produce an XML document identical to the original one, without the <?xml version="1.0"?> processing instruction.

The following cases show different ways of writing this template rule. The point is to show the order in which the XPath operators bind to an element.

Case 1: () binds tighter than []

The following template rule selects the first <y> element in the document order, from all the <y> elements in the source document.

<xsl:template match="/test">
      <xsl:apply-templates select="(//y)[1]"/>
   </xsl:template>

The result is as follows:

<y>y31</y>

Case 2: [] binds tighter than / or //

The following template rule selects all the <y> elements that are the first among their siblings.

<xsl:template match="/test">
   <xsl:apply-templates select="//y[1]"/>
</xsl:template>

The result is as follows:

<y>y31</y>

<y>y21</y>

<y>y11</y>

<y>y03</y>

Case 3: and, not

The following template rule selects all the <x> elements that have no <x> child elements, that have an <x> parent element, and that do not have any attributes.

<xsl:template match="/test">
   <xsl:apply-templates select=
    "//x[./ancestor::*[name()='x'] and *[name()!='x'] and not(@*)]"/>
</xsl:template>

The result is a single <x> element, listed below with its children:

<x>
   <y>y31</y>
   <y>y32</y>
</x>

Case 4: or, and, not

The following template rule selects each <x> elements that is a child of an <x> element; or, that is not a parent of an <x> element and has no attributes.

<xsl:template match="/test">
   <xsl:apply-templates select=
    "//x[./ancestor::*[name()='x'] or *[name()!='x'] and not(@*)]"/>
</xsl:template>

The result is a node set containing the following <x> elements, listed below with its children:

<x a="2" b="B">
  <x>
     <y>y31</y>
     <y>y32</y>
  </x>
</x>
<x>
  <y>y31</y>
  <y>y32</y>
</x>
<x a="2">
  <y>y21</y>
  <y>y22</y>
</x>
<x>
  <y>y03</y>
  <y>y04</y>
</x>

Case 5: and, or, not

The following template rule selects each <x> element that is a child of an <x> element but not a parent of an <x> element; or, that has no attributes.

<xsl:template match="/test">
   <xsl:apply-templates select=
    "//x[./ancestor::*[name()='x'] and *[name()!='x'] or not(@*)]"/>
</xsl:template>

The result is a node set containing the following <x> elements, listed below with its children:

<x>
   <y>y31</y>
   <y>y32</y>
</x>
<x a="2">
  <y>y21</y>
  <y>y22</y>
</x>
<x>
  <y>y03</y>
  <y>y04</y>
</x>