Please check the errata for any errors or issues reported since publication.
See also translations.
This document is also available in these non-normative formats: Specification in XML format and XML function catalog.
Copyright © 2000 W3C® (MIT, ERCIM, Keio, Beihang). W3C liability, trademark and document use rules apply.
This document defines constructor functions, operators, and functions on the datatypes defined in [XML Schema Part 2: Datatypes Second Edition] and the datatypes defined in [XQuery and XPath Data Model (XDM) 3.1]. It also defines functions and operators on nodes and node sequences as defined in the [XQuery and XPath Data Model (XDM) 3.1]. These functions and operators are defined for use in [XML Path Language (XPath) 4.0] and [XQuery 4.0: An XML Query Language] and [XSL Transformations (XSLT) Version 4.0] and other related XML standards. The signatures and summaries of functions defined in this document are available at: http://www.w3.org/2005/xpath-functions/.
A summary of changes since version 3.1 is provided at G Changes since 3.1.
This version of the specification is work in progress. It is produced by the QT4 Working Group, officially the W3C XSLT 4.0 Extensions Community Group. Individual functions specified in the document may be at different stages of review, reflected in their History notes. Comments are invited, in the form of GitHub issues at https://github.com/qt4cg/qtspecs.
The publications of this community group are dedicated to our co-chair, Michael Sperberg-McQueen (1954–2024).
A sequence is an ordered collection of zero or more items. An item is a node, an atomic item, or a function, such as a map or an array. The terms sequence and item are defined formally in [XQuery 4.0: An XML Query Language] and [XML Path Language (XPath) 4.0].
The functions in this section perform comparisons between the items in one or more sequences.
| Function | Meaning |
|---|---|
fn:atomic-equal | Determines whether two atomic items are equal, under the rules used for comparing keys in a map. |
fn:deep-equal | This function assesses whether two sequences are deep-equal to each other. To be deep-equal, they must contain items that are pairwise deep-equal; and for two items to be deep-equal, they must either be atomic items that compare equal, or nodes of the same kind, with the same name, whose children are deep-equal, or maps with matching entries, or arrays with matching members. |
fn:compare | Returns -1, 0, or 1, depending on whether the first value is less than, equal to, or greater than the second value. |
fn:distinct-values | Returns the values that appear in a sequence, with duplicates eliminated. |
fn:duplicate-values | Returns the values that appear in a sequence more than once. |
fn:index-of | Returns a sequence of positive integers giving the positions within the sequence $input of items that are equal to $target. |
fn:starts-with-subsequence | Determines whether one sequence starts with another, using a supplied callback function to compare items. |
fn:ends-with-subsequence | Determines whether one sequence ends with another, using a supplied callback function to compare items. |
fn:contains-subsequence | Determines whether one sequence contains another as a contiguous subsequence, using a supplied callback function to compare items. |
When comments and processing instructions are ignored, any text nodes either side of the comment or processing instruction are now merged prior to comparison. [Issue 930 PR 933 16 January 2024]
The $options parameter has been added, absorbing the $collation parameter. [Issues 934 1167 PR 1191 21 May 2024]
A callback function can be supplied for comparing individual items. [Issues 99 1142 PRs 1120 1150 9 April 2024]
This function assesses whether two sequences are deep-equal to each other. To be deep-equal, they must contain items that are pairwise deep-equal; and for two items to be deep-equal, they must either be atomic items that compare equal, or nodes of the same kind, with the same name, whose children are deep-equal, or maps with matching entries, or arrays with matching members.
fn:deep-equal( | ||
$input1 | as , | |
$input2 | as , | |
$options | as | := {} |
) as | ||
The two-argument form of this function is deterministic, context-dependent, and focus-independent. It depends on collations, and implicit timezone.
The three-argument form of this function is deterministic, context-dependent, and focus-independent. It depends on collations, and static base URI, and implicit timezone.
The $options argument, if present, defines additional parameters controlling how the comparison is done. If it is supplied as a map, then the option parameter conventions apply.
For backwards compatibility reasons, the $options argument can also be set to a string containing a collation name. Supplying a string $S for this argument is equivalent to supplying the map { 'collation': $S }. Omitting the argument, or supplying the empty sequence, is equivalent to supplying an empty map.
If the two sequences ($input1 and $input2) are both empty, the function returns true.
If the two sequences are of different lengths, the function returns false.
If the two sequences are of the same length, the comparison is controlled by the ordered option:
By default, the option is true: The function returns true if and only if every item in the sequence $input1 is deep-equal to the item at the same position in the sequence $input2.
If the option is set to false, the function returns false if and only if every item in the sequence $input1 is deep-equal to an item at some position in the sequence $input2, and vice versa.
The rules for deciding whether two items are deep-equal appear below.
The entries that may appear in the $options map are as follows. The detailed rules for the interpretation of each option appear later.
record( | |
base-uri? | as xs:boolean, |
collation? | as xs:string, |
comments? | as xs:boolean, |
debug? | as xs:boolean, |
id-property? | as xs:boolean, |
idrefs-property? | as xs:boolean, |
in-scope-namespaces? | as xs:boolean, |
items-equal? | as fn(item(), item()) as xs:boolean?, |
map-order? | as xs:boolean, |
namespace-prefixes? | as xs:boolean, |
nilled-property? | as xs:boolean, |
normalization-form? | as xs:string?, |
ordered? | as xs:boolean, |
processing-instructions? | as xs:boolean, |
timezones? | as xs:boolean, |
type-annotations? | as xs:boolean, |
type-variety? | as xs:boolean, |
typed-values? | as xs:boolean, |
unordered-elements? | as xs:QName*, |
whitespace? | as enum("preserve", "strip", "normalize") |
) | |
| Key | Meaning |
|---|---|
| Determines whether the base-uri of a node is significant.
|
| Identifies a collation which is used at all levels of recursion when strings are compared (but not when names are compared), according to the rules in 5.3.6 Choosing a collation. If the argument is not supplied, or if it is empty, then the default collation from the dynamic context of the caller is used.
|
| Determines whether comments are significant.
|
| Requests diagnostics in the case where the function returns false. When this option is set and the two inputs are found to be not equal, the implementation should output messages (in an implementation-dependent format and to an implementation-dependent destination) indicating the nature of the differences that were found.
|
| Determines whether the id property of elements and attributes is significant.
|
| Determines whether the idrefs property of elements and attributes is significant.
|
| Determines whether the in-scope namespaces of elements are significant.
|
| A user-supplied function to test whether two items are considered equal. The function can return true or false to indicate that two items are or are not equal, overriding the normal rules that would apply to those items; or it can return an empty sequence, to indicate that the normal rules should be followed. Note that returning () is not equivalent to returning false.
|
| Determines whether the order of entries in maps is significant.
|
| Determines whether namespace prefixes in xs:QName values (particularly the names of elements and attributes) are significant.
|
| Determines whether the nilled property of elements and attributes is significant.
|
| If present, indicates that text and attributes are converted to the specified Unicode normalization form prior to comparison. The value is as for the corresponding argument of fn:normalize-unicode.
|
| Controls whether the top-level order of the items of the input sequences is considered.
|
| Determines whether processing instructions are significant.
|
| Determines whether timezones in date/time values are significant.
|
| Determines whether type annotations are significant.
|
| Determines whether the variety of the type annotation of an element (whether it has complex content or simple content) is significant.
|
| Determines whether nodes are compared using their typed values rather than their string values.
|
| A list of QNames of elements considered to be unordered: that is, their child elements may appear in any order.
|
| Determines the extent to which whitespace is treated as significant. The value preserve retains all whitespace. The value strip ignores text nodes consisting entirely of whitespace. The value normalize ignores whitespace text nodes in the same way as the strip option, and additionally compares text and attribute nodes after normalizing whitespace in accordance with the rules of the fn:normalize-space function. The detailed rules, given below, also take into account type annotations and xml:space attributes.
|
Note:
As a general rule for boolean options (but not invariably), the value true indicates that the comparison is more strict.
In the following rules, where a recursive call on fn:deep-equal is made, this is assumed to use the same values of $options as the original call.
The rules reference a function equal-strings which compares two xs:string or xs:anyURI values as follows:
If the whitespace option is set to normalize, then each string is processed by calling the fn:normalize-space function.
If the normalization-form option is present, each string is then normalized by calling the fn:normalize-unicode function, supplying the specified normalization form.
The two strings are then compared for equality under the requested collation.
More formally, the equal-strings function is equivalent to the following implementation in XQuery:
declare function equal-strings(
$string1 as xs:string,
$string2 as xs:string,
$options as map(*)
) as xs:boolean {
let $n1 := if ($options?normalization-form)
then normalize-unicode(?, $options?normalization-form)
else identity#1
let $n2 := if ($options?whitespace = "normalize")
then normalize-space#1
else identity#1
return compare($n1($n2($string1)), $n1($n2($string2)), $options?collation) eq 0
}The rules for deciding whether two items $i1 and $i2 are deep-equal are as follows.
The two items are first compared using the function supplied in the items-equal option. If this returns true then the items are deep-equal. If it returns false then the items are not deep-equal. If it returns an empty sequence (which is always the case if the option is not explicitly specified) then the two items are deep-equal if one or more of the following conditions are true:
All of the following conditions are true:
$i1 is an atomic item.
$i2 is an atomic item.
Either the type-annotations option is false, or both atomic items have the same type annotation.
One of the following conditions is true:
If both $i1 and $i2 are instances of xs:string, xs:untypedAtomic, or xs:anyURI, equal-strings($i1, $i2, $collation, $options) returns true.
If both $i1 and $i2 are instances of xs:date, xs:time or xs:dateTime, $i1 eq $i2 returns true.
If both $i1 and $i2 are instances of xs:hexBinary or xs:base64Binary, $i1 eq $i2 returns true.
Otherwise, fn:atomic-equal($i1, $i2) returns true.
Note:
If $i1 and $i2 are not comparable, that is, if the expression ($i1 eq $i2) would raise an error, then the function returns false; it does not report an error.
One of the following conditions is true:
Option namespace-prefixes is false.
Neither $i1 nor $i2 is of type xs:QName or xs:NOTATION.
$i1 and $i2 are qualified names with the same namespace prefix.
One of the following conditions is true:
Option timezones is false.
Neither $i1 nor $i2 is of type xs:date, xs:time, xs:dateTime, xs:gYear, xs:gYearMonth, xs:gMonth, xs:gMonthDay, or xs:gDay.
Neither $i1 nor $i2 has a timezone component.
Both $i1 and $i2 have a timezone component and the timezone components are equal.
All of the following conditions are true:
$i1 is a map.
$i2 is a map.
Both maps have the same number of entries.
For every entry in the first map, there is an entry in the second map that:
has the same key (note that the collation is not used when comparing keys), and
has the same associated value (compared using the fn:deep-equal function, recursively).
Either map-order is false, or the entries in both maps appear in the same order, that is, the Nth key in the first map is the same key as the Nth key in the second map, for all N.
Note:
It is not required that the order of entries in the two maps should be the same.
All the following conditions are true:
$i1 is an array.
$i2 is an array.
Both arrays have the same number of members (array:size($i1) eq array:size($i2)).
Members in the same position of both arrays are deep-equal to each other: that is, every $p in 1 to array:size($i1) satisfies deep-equal($i1($p), $i2($p), $collation, $options).
All the following conditions are true:
$i1 is a function item and is not a map or array.
$i2 is a function item and is not a map or array.
$i1 and $i2 have the same function identity. The concept of function identity is explained in Section 7.1 Function ItemsDM.
All the following conditions are true:
$i1 is a node.
$i2 is a node.
Both nodes have the same node kind.
Either the base-uri option is false, or both nodes have the same value for their base URI property, or both nodes have an absent base URI.
Let significant-children($parent) be the sequence of nodes obtained by applying the following steps to the children of $parent, in turn:
Comment nodes are discarded if the option comments is false.
Processing instruction nodes are discarded if the option processing-instructions is false.
Adjacent text nodes are merged.
Whitespace-only text nodes are discarded if both the following conditions are true:
The option whitespace is set to strip or normalize; and
The text node is not within the scope of an element that has the attribute xml:space="preserve".
Note:
Whitespace text nodes will already have been discarded if $parent is a schema-validated element node whose type annotation is a complex type with an element-only or empty content model.
One of the following conditions is true.
Both nodes are document nodes, and the sequence significant-children($i1) is deep-equal to the sequence significant-children($i2).
Both nodes are element nodes, and all the following conditions are true:
The two nodes have the same name, that is (node-name($i1) eq node-name($i2)).
Either the option namespace-prefixes is false, or both element names have the same prefix.
Either the option in-scope-namespaces is false, or both element nodes have the same in-scope namespace bindings.
Either the option type-annotations is false, or both element nodes have the same type annotation.
Either the option id-property is false, or both element nodes have the same value for their is-id property.
Either the option idrefs-property is false, or both element nodes have the same value for their is-idrefs property.
Either the option nilled-property is false, or both element nodes have the same value for their nilled property.
One of the following conditions is true:
The option type-variety is false.
Both nodes are annotated as having simple content. For this purpose simple content means either a simple type or a complex type with simple content.
Both nodes are annotated as having complex content. For this purpose complex content means a complex type whose variety is mixed, element-only, or empty.
Note:
It is a consequence of this rule that, by default, validating a document D against a schema will usually (but not necessarily) result in a document that is not deep-equal to D. The exception is when the schema allows all elements to have mixed content.
The two nodes have the same number of attributes, and for every attribute $a1 in $i1/@* there exists an attribute $a2 in $i2/@* such that node-name($a1) eq node-name($a2) and $a1 and $a2 are deep-equal.
Note:
Attributes, like other items, may be compared using the supplied items-equal function. However, this function will not be called to compare two attribute nodes unless they have the same name.
One of the following conditions holds:
Both element nodes are annotated as having simple content (as defined above), the typed-values option is true, and the typed value of $i1 is deep-equal to the typed value of $i2.
Note:
The typed value of an element node is used only when the element has simple content, which means that no error can occur as a result of atomizing a node with no typed value.
Both element nodes are annotated as having simple content (as defined above), the typed-values option is false, and the equal-strings function returns true when applied to the string value of $i1 and the string value of $i2.
Both element nodes have a type annotation that is a complex type with element-only, mixed, or empty content, the (common) element name is not present in the unordered-elements option, and the sequence significant-children($i1) is deep-equal to the sequence significant-children($i2).
Both element nodes have a type annotation that is a complex type with element-only, mixed, or empty content, the (common) element name is present in the unordered-elements option, and the sequence significant-children($i1) is deep-equal to some permutation of the sequence significant-children($i2).
Note:
Elements annotated as xs:untyped fall into this category.
Including an element name in the unordered-elements list is unlikely to be useful except when the relevant elements have element-only content, but this is not a requirement: the rules apply equally to elements with mixed content, or even (trivially) to elements with empty content.
Both nodes are attribute nodes, and all the following conditions are true:
The two attribute nodes have the same name, that is (node-name($i1) eq node-name($i2)).
Either the option namespace-prefixes is false, or both attribute names have the same prefix.
Either the option type-annotations is false, or both attribute nodes have the same type annotation.
Either the option id-property is false, or both attribute nodes have the same value for their is-id property.
Either the option idrefs-property is false, or both attribute nodes have the same value for their is-idrefs property.
Let T be true if the option typed-value is true and both attributes $i1 and $i2 have a type annotation other than xs:untypedAtomic.
Then either T is true and the typed value of $i1 is deep-equal to the typed value of $i2, or T is false and the equal-strings function returns true when applied to the string value of $i1 and the string value of $i2.
Both nodes are processing instruction nodes, and all the following conditions are true:
The two nodes have the same name, that is (node-name($i1) eq node-name($i2)).
The equal-strings function returns true when applied to the string value of $i1 and the string value of $i2.
Both nodes are namespace nodes, and all the following conditions are true:
The two nodes either have the same name or are both nameless, that is fn:deep-equal(node-name($i1), node-name($i2)).
The string value of $i1 is equal to the string value of $i2 when compared using the Unicode codepoint collation.
Note:
Namespace nodes are not considered directly unless they appear in the top-level sequences passed explicitly to the fn:deep-equal function.
Both nodes are comment nodes, and the equal-strings function returns true when applied to their string values.
Both nodes are text nodes, and the equal-strings function returns true when applied to their string values.
In all other cases the result is false.
A type error is raised [err:XPTY0004]XP if the value of $options includes an entry whose key is defined in this specification, and whose value is not of the permitted type for that key.
A dynamic error is raised [err:FOJS0005] if the value of $options includes an entry whose key is defined in this specification, and whose value is not a permitted value for that key.
By default, whitespace in text nodes and attributes is considered significant. There are various ways whitespace differences can be ignored:
If nodes have been schema-validated, setting the typed-values option to true causes the typed values rather than the string values to be compared. This will typically cause whitespace to be ignored except where the type of the value is xs:string.
Setting the whitespace option to normalize causes all text and attribute nodes to have leading and trailing whitespace removed, and intermediate whitespace reduced to a single character.
By default, two nodes are not required to have the same type annotation, and they are not required to have the same in-scope namespaces. They may also differ in their parent, their base URI, and the values returned by the is-id and is-idrefs accessors (see Section 6.7.5 is-id AccessorDM and Section 6.7.6 is-idrefs AccessorDM). The order of children is significant, but the order of attributes is insignificant.
By default, the contents of comments and processing instructions are significant only if these nodes appear directly as items in the two sequences being compared. The content of a comment or processing instruction that appears as a descendant of an item in one of the sequences being compared does not affect the result. In previous versions of this specification, the presence of a comment or processing instruction, if it caused text to be split across two text nodes, might affect the result; this has been changed in 4.0 so that adjacent text nodes are merged after comments and processing instructions have been stripped.
Comparing items of different kind (for example, comparing an atomic item to a node, or a map to an array, or an integer to an xs:date) returns false, it does not return an error. So the result of fn:deep-equal(1, current-dateTime()) is false.
The items-equal callback function may be used to override the default rules for comparing individual items. For example, it might return true unconditionally when comparing two @timestamp attributes, if there is no expectation that the two trees will have identical timestamps. Given two nodes $n1 and $n2, it might compare them using the is operator, so that instead of comparing the descendants of the two nodes, the function simply checks whether they are the same node. Given two function items $f1 and $f2 it might return true unconditionally, knowing that there is no effective way to test if the functions are equivalent. Given two numeric values, it might return true if they are equal to six decimal places.
It is good practice for the items-equal callback function to be reflexive, symmetric, and transitive; if it is not, then the fn:deep-equal function itself will lack these qualities. Reflexive means that every item (including NaN) should be equal to itself; symmetric means that items-equal(A, B) should return the same result as items-equal(B, A), and transitive means that items-equal(A, B) and items-equal(B, C) should imply items-equal(A, C).
Setting the ordered option to false or supplying the unordered-elements option may result in poor performance when comparing long sequences, especially if the items-equal callback function is supplied.
| Variables | |
|---|---|
let $at := <attendees> <name last="Parker" first="Peter"/> <name last="Barker" first="Bob"/> <name last="Parker" first="Peter"/> </attendees> | |
| Expression: |
|
|---|---|
| Result: | false() |
| Expression: |
|
| Result: | false() |
| Expression: |
|
| Result: | true() |
| Expression: |
|
| Result: | false() |
| Expression: | deep-equal(
$at//name[@first="Bob"],
$at//name[@last="Barker"],
options := { 'items-equal': op('is') }
) |
| Result: | true() (Tests whether the two input sequences contain exactly the same nodes.) |
| Expression: |
|
| Result: | true() |
| Expression: |
|
| Result: | false() |
| Expression: | deep-equal(
{ 1: 'a', 2: 'b' },
{ 2: 'b', 1: 'a' }
) |
| Result: | true() |
| Expression: | deep-equal(
(1, 2, 3, 4),
(1, 4, 3, 2),
options := { 'ordered': false() }
) |
| Result: | true() |
| Expression: | deep-equal(
(1, 1, 2, 3),
(1, 2, 3, 3),
options := { 'ordered': false() }
) |
| Result: | false() |
| Expression: | deep-equal(
parse-xml("<a xmlns='AA'/>"),
parse-xml("<p:a xmlns:p='AA'/>")
) |
| Result: | true() (By default, namespace prefixes are ignored). |
| Expression: | deep-equal(
parse-xml("<a xmlns='AA'/>"),
parse-xml("<p:a xmlns:p='AA'/>"),
options := { 'namespace-prefixes': true() }
) |
| Result: | false() (False because the namespace prefixes differ). |
| Expression: | deep-equal(
parse-xml("<a xmlns='AA'/>"),
parse-xml("<p:a xmlns:p='AA'/>"),
options := { 'in-scope-namespaces': true() }
) |
| Result: | false() (False because the in-scope namespace bindings differ). |
| Expression: | deep-equal(
parse-xml("<a><b/><c/></a>"),
parse-xml("<a><c/><b/></a>")
) |
| Result: | false() (By default, order of elements is significant). |
| Expression: | deep-equal(
parse-xml("<a><b/><c/></a>"),
parse-xml("<a><c/><b/></a>"),
options := { 'unordered-elements': xs:QName('a') }
) |
| Result: | true() (The |
| Expression: | deep-equal(
parse-xml("<para style='bold'><span>x</span></para>"),
parse-xml("<para style=' bold'> <span>x</span></para>")
) |
| Result: | false() (By default, both the leading whitespace in the |
| Expression: | deep-equal(
parse-xml("<para style='bold'><span>x</span></para>"),
parse-xml("<para style=' bold'> <span>x</span></para>"),
options := { 'whitespace': 'normalize' }
) |
| Result: | true() (The |
| Expression: | deep-equal(
(1, 2, 3),
(1.0007, 1.9998, 3.0005),
options := { 'items-equal': fn($x, $y) {
if (($x, $y) instance of xs:numeric+) {
abs($x - $y) lt 0.001
}
} }
) |
| Result: | true() (For numeric values, the callback function tests whether they are approximately equal. For any other items, it returns an empty sequence, so the normal comparison rules apply.) |
| Expression: | deep-equal(
(1, 2, 3, 4, 5),
(1, 2, 3, 8, 5),
options := { 'items-equal': fn($x, $y) {
trace((), `comparing { $x } and { $y }`)
} }
) |
| Result: | false() (The callback function traces which items are being compared, without changing the result of the comparison.) |