13

I've seen on SO and other places that the following is supposed to work (this example is lifted directly from O'Reilly's XSLT Cookbook):

(: intersection :)
$set1[count(. | $set2) = count($set2)]

(: difference :)
$set1[count(. | $set2) != count($set2)]

and it looks like it should be OK, however this seems to fail when used with actual paths rather than variables. For example, given the following document

<a>
  <new>
    <val>1</val>
    <val>2</val>
  </new>
  <old>
    <val>2</val>
    <val>3</val>
  </old>
</a>

and the XPath expression /a/new/val[count(. | /a/old/val)=count(/a/old/val)]/text() I would expect to get the node-set { 2 } but instead am getting { 1 2 }. Any ideas what I'm doing wrong?

Ian Phillips
  • 163
  • 1
  • 6
  • Good question, +1. See my answer for explanation of your problem and for a simple and easy one-liner XPath expression - solution -- no other answer proposes a solution at this moment. :) – Dimitre Novatchev Aug 25 '11 at 04:21

2 Answers2

11

The formulas for node-set intersection use node-identity, not value identity.

Two nodes are identical if and only if count($n1|$n2) =1

However, you want to intersect based on value identity.

Solution:

Use:

/a/new/val[. = /a/old/val]

this selects any /a/new/val for which there exists at least one /a/old/val element such that the string values of these two elements is the same.

Dimitre Novatchev
  • 240,661
  • 26
  • 293
  • 431
1

Remark that intersection always results in a single node-set composed by the nodes in common between the two original nodes-sets.

Also note that two nodes with same name and content are to be treated as two distinct nodes. Thus /a/new/val/text() and /a/old/val/text() have same value but they are completely distinct text nodes.

Thus your current intersection:

/a/new/val[count(. | /a/old/val)=count(/a/old/val)]

should evaluate to an empty node-set, because you are intersecting two nodes-sets without any node in common (count() operation will never match). You are doing something like this:

/a/new/val/a/old/val = ∅

While /a/new/a/old/preceding::new will produce new.

Emiliano Poggi
  • 24,390
  • 8
  • 55
  • 67