View Old View New View Both View Only Previous Next

This draft contains only sections that have differences from the version that it modified.

W3C

XPath and XQuery Functions and Operators 4.0

W3C Editor's Draft 23 February 2026

This version:
https://qt4cg.org/specifications/xpath-functions-40/
Latest version of XPath and XQuery Functions and Operators 4.0:
https://qt4cg.org/specifications/xpath-functions-40/
Most recent Recommendation of XPath and XQuery Functions and Operators:
https://www.w3.org/TR/2017/REC-xpath-functions-31-20170321/
Editor:
Michael Kay, Saxonica <http://www.saxonica.com/>

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.


Abstract

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 H Changes since 3.1.

Status of this Document

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.

Dedication

The publications of this community group are dedicated to our co-chair, Michael Sperberg-McQueen (1954–2024).


12 Processing nodes

12.2 Other properties of nodes

This section specifies further functions that return properties of nodes. Nodes are formally defined in 6 Nodes DM31.

FunctionMeaning
fn:has-childrenReturns true if the supplied GNode has one or more child nodes (of any kind).
fn:in-scope-namespacesReturns the in-scope namespaces of an element node, as a map.
fn:in-scope-prefixesReturns the prefixes of the in-scope namespaces for an element node.
fn:langThis function tests whether the language of $node, or the context value if the second argument is omitted, as specified by xml:lang attributes is the same as, or is a sublanguage of, the language specified by $language.
fn:local-nameReturns the local part of the name of $node as an xs:string that is either the zero-length string, or has the lexical form of an xs:NCName.
fn:nameReturns the name of a node, as an xs:string that is either the zero-length string, or has the lexical form of an xs:QName.
fn:namespace-uriReturns the namespace URI part of the name of $node, as an xs:anyURI value.
fn:namespace-uri-for-prefixReturns the namespace URI of one of the in-scope namespaces for $element, identified by its namespace prefix.
fn:pathReturns a path expression that can be used to select the supplied node relative to the root of its containing document.
fn:rootReturns the root of the tree to which $node belongs. The function can be applied both to XNodesDM and to JNodesDM.
fn:siblingsReturns the supplied GNode together with its siblings, in document order.

12.2.9 fn:path

Changes in 4.0  

  1. Options are added to customize the form of the output.  [Issues 332 1660 PRs 1620 1886 29 November 2024]

  2. The function is extended to handle JNodes.  [Issue 2100  5 August 2025]

Summary

Returns a path expression that can be used to select the supplied node relative to the root of its containing document.

Signature
fn:path(
$nodeas gnode()?:= .,
$optionsas map(*)?:= {}
) as xs:string?
Properties

The zero-argument form of this function is deterministic, context-dependent, and focus-dependent.

The one-argument form of this function is deterministic, context-independent, and focus-independent.

The two-argument form of this function is deterministic, context-independent, and focus-independent.

Rules

The behavior of the function if the $nodeargument is omitted is exactly the same as if the context value (.) had been passed as the argument.

If $node is the empty sequence, the function returns the empty sequence.

The $options argument, if present, defines additional parameters controlling how the output is formatted. The option parameter conventions apply. The options available are as follows:

record(
origin?as gnode()?,
lexical?as xs:boolean,
namespaces?as map((xs:NCName | enum('')), xs:anyURI)?,
indexes?as xs:boolean
)
KeyMeaning

origin?

A GNode, which must be an ancestor of $node. If present, the returned path will be a relative path that selects $node starting from the supplied origin node, rather than from the root of the containing tree.
  • Type: gnode()?

  • Default: ()

lexical?

If true, the names of element nodes in the path are represented by the result of a call on the name function applied to each element. The result in this case does not contain sufficient information to identify the namespace URI of the element.
  • Type: xs:boolean

  • Default: false()

namespaces?

A map from namespace prefixes to namespace URIs, such as might be returned by the function fn:in-scope-namespaces. If a prefix is available for a given URI, it is used in preference to using Q{uri}local notation.
  • Type: map((xs:NCName | enum('')), xs:anyURI)?

  • Default: ()

indexes?

If true, the returned path includes the index positions of nodes. If false, only the node names are included.
  • Type: xs:boolean

  • Default: true()

Let R be the GNode supplied in the origin option, or the root GNode of the tree containing $node otherwise.

If $node is a document node, or a JNode with no parent, the function returns the string "/".

Otherwise, the function returns a string that consists of a sequence of steps, one for each ancestor-or-self of $node that is not an ancestor-or-self of R.

If R is an XNode other than a document node and the origin option is absent or empty, then this string is preceded by a string notionally representing a call to the fn:root function, expressed as follows:

  • If the lexical option is present with the value true, then the string "fn:root()".

  • If the namespaces option is present and defines a mapping from a non empty prefix P to the namespace URI http://www.w3.org/2005/xpath-functions, then "P:root()"

  • If the namespaces option is present and defines a mapping from the empty string to the namespace URI http://www.w3.org/2005/xpath-functions, then "root()"

  • Otherwise, "Q{http://www.w3.org/2005/xpath-functions}root()".

Each step is the concatenation of:

  1. The character "/", which is omitted for the first step if the origin option is present;

  2. A string whose form depends on the kind of node selected by that step, as follows:

    1. For an element node, the concatenation of:

      1. A representation of the element name, chosen as follows:

        1. If the lexical option is present with the value true, then the result of applying the name function to the element node.

        2. Otherwise, if the namespaces option is present and the element is in a namespace U and the namespaces option includes a mapping from a prefix P to the namespace U, then the string P:L, where L is the local part of the element name. If there is more than one such prefix, then one of them is chosen arbitrarily.

        3. Otherwise, if the namespaces option is present and the element is in a namespace U and the namespaces option includes a mapping from the zero-length string to the namespace U, then the local part of the element name.

        4. Otherwise, if the namespaces option is present and the element is in no namespace and the namespaces option includes no mapping from the zero-length string to any namespace, then the local part of the element name.

        5. Otherwise, the string Q{U}L, where U is the namespace URI of the element name or the empty string if the element is in no namespace, and L is the local part of the element name.

      2. Unless the indexes option is present with the value false, a string in the form [position] where position is an integer representing the one-based position of the selected node among its like-named siblings.

    2. For an attribute node, the concatenation of:

      1. The character "@"

      2. If the lexical option is present with the value true, then the result of applying the name function to the attribute node.

      3. Otherwise, if the attribute node is in no namespace, the local part of the attribute name.

      4. Otherwise, if the namespaces option is present, and if it includes a mapping from a non-empty namespace prefix P to the namespace URI of the attribute, then a string in the form P:L, where L is the local part of the attribute name. If there is more than one such prefix, then one of them is chosen arbitrarily.

      5. Otherwise, the string Q{U}L, where U is the namespace URI of the attribute name, and L is the local part of the attribute name.

    3. For a text node: text()[position] where position is an integer representing the position of the selected node among its text node siblings.

      The suffix [position] is omitted if the indexes option is present with the value false.

    4. For a comment node: comment()[position] where position is an integer representing the position of the selected node among its comment node siblings.

      The suffix [position] is omitted if the indexes option is present with the value false.

    5. For a processing-instruction node: processing-instruction(local)[position] where local is the name of the processing instruction node and position is an integer representing the position of the selected node among its like-named processing-instruction node siblings.

      The suffix [position] is omitted if the indexes option is present with the value false.

    6. For a namespace node:

      1. If the namespace node has a name: namespace::prefix, where prefix is the local part of the name of the namespace node (which represents the namespace prefix).

      2. If the namespace node has no name (that is, if it represents the default namespace): namespace::*[Ulocal-name() = ""]

        Here Ulocal-name() represents a call on the function fn:local-name and is formatted using the same conventions as the call on fn:root described earlier.

    7. For a JNode where the ·content· property of the parent is an array, then as the string *[N] where N is the value of the ·selector· property.

    8. For any other JNode (including the case where the ·content· property of the parent is a map):

      1. If the value is an xs:string, xs:untypedAtomic, or xs:anyURI that is castable to xs:NCName, then the result of casting the value to xs:NCName.

      2. If the value is an xs:string, xs:untypedAtomic, or xs:anyURI that is not castable to xs:NCName, then then as the string get("S") where S is the string value.

      3. If the value is numeric, then as the string get(N) where N is the result of casting the numeric value to xs:string.

      4. If the value is an xs:QName, then as the string get(#Q{uri}local) where uri and local are the namespace URI and local name parts of the QName.

      5. If the value is an xs:boolean, then as the string get(true()) or get(false()).

      6. If the value is of any other type, then as the string get(xs:T("S")) where T is the local part of the most specific built-in atomic type of which the value is an instance, and S is the result of casting the value to xs:string.

      TODO: Better handling of the case where the parent is neither a map nor an array, for example where it is a sequence of several maps or several arrays. It's hard to provide a better path for these when there is no AxisStep for selecting within such values.

Error Conditions

The following errors may be raised when $node is omitted:

  • If the context value is absentDM, type error [err:XPDY0002]XP

  • If the context value is not an instance of the sequence type node()?, type error [err:XPTY0004]XP.

If the value of the origin option is a node that is not an ancestor of $node (or in the absence of $node, the context value), dynamic error [err:FOPA0001].

Notes

Using the namespaces option to shorten the generated path is often convenient, but the resulting path may be unusable if the input tree contains multiple bindings for the same prefix.

Similarly, using the lexical option is convenient if there is no need for precise namespace information: it is especially suitable when the containing node tree declares no namespaces.

If the supplied argument is a map or an array, it will automatically be coerced to a JNode. This however is not useful, because this will be a root JNode, yielding the path /.

Examples
Variables
let $e := document {            
  <p xmlns="http://example.com/one" xml:lang="de" author="Friedrich von Schiller">
Freude, schöner Götterfunken,<br/>
Tochter aus Elysium,<br/>
Wir betreten feuertrunken,<br/>
Himmlische, dein Heiligtum.
</p>}
let $emp := 
  <employee xml:id="ID21256">
     <empnr>E21256</empnr>
     <first>John</first>
     <last>Brown</last>
  </employee>
Expression:
path($e)
Result:
'/'
Expression:
path($e/*:p)
Result:
'/Q{http://example.com/one}p[1]'
Expression:
path($e/*:p, { 'namespaces': in-scope-namespaces($e/*) })
Result:
'/p[1]'
Expression:
path($e/*:p, { 'indexes': false() })
Result:
'/Q{http://example.com/one}p'
Expression:
path($e/*:p/@xml:lang)
Result:
'/Q{http://example.com/one}p[1]/@Q{http://www.w3.org/XML/1998/namespace}lang'
Expression:
path($e//@xml:lang, { 'namespaces': in-scope-namespaces($e/*) })
Result:
'/p[1]/@xml:lang'
Expression:
path($e/*:p/@author)
Result:
'/Q{http://example.com/one}p[1]/@author'
Expression:
path($e/*:p/*:br[2])
Result:
'/Q{http://example.com/one}p[1]/Q{http://example.com/one}br[2]'
Expression:
path($e/*:p/*:br[2], {
  'namespaces': { 'N': 'http://example.com/one' },
  'indexes': false() 
})
Result:
'/N:p/N:br'
Expression:
path($e//text()[starts-with(normalize-space(), 'Tochter')])
Result:
'/Q{http://example.com/one}p[1]/text()[2]'
Expression:
path($e/*:p/*:br[2], { 'lexical': true() })
Result:
'/p[1]/br[2]'
Expression:
path($e/*:p/*:br[2], { 'lexical': true(), 'origin': $e/*:p })
Result:
'br[2]'
Expression:
path($emp)
Result:
'Q{http://www.w3.org/2005/xpath-functions}root()'
Expression:
path($emp/@xml:id)
Result:
'Q{http://www.w3.org/2005/xpath-functions}root()/@Q{http://www.w3.org/XML/1998/namespace}id'
Expression:
path($emp/empnr)
Result:
'Q{http://www.w3.org/2005/xpath-functions}root()/Q{}empnr[1]'
Expression:
path($emp/empnr, { 'lexical': true() })
Result:
'fn:root()/empnr[1]'
Expression:
path($emp/empnr, {
  'namespaces': {
    'fn': 'http://www.w3.org/2005/xpath-functions',
    '': ''
  }
})
Result:
'fn:root()/empnr[1]'
Expression:
let $in := [{"b":[3,4]}]
return path($in/*[1]/b/*[2])
Result:
"/*[1]/b/*[2]
"/*[1]/b/*[2]"
Expression:
let $in := [[{'a':1}], [{'a':2}]]
return path($in//a[. = 2])
Result:
"/*[2]/*[1]/a
"/*[2]/*[1]/a"

12.3 Functions on sequences of nodes

This section specifies functions on sequences of nodes.

FunctionMeaning
fn:distinct-ordered-nodesRemoves duplicate GNodes and sorts the input into document order.
fn:innermostReturns every GNode within the input sequence that is not an ancestor of another member of the input sequence; the GNodes are returned in document order with duplicates eliminated.
fn:outermostReturns every GNode within the input sequence that has no ancestor that is itself a member of the input sequence; the nodes are returned in document order with duplicates eliminated.

12.3.2 fn:innermost

Changes in 4.0  

  1. Generalized to work with JNodes as well as XNodes.  [Issue 2100 ]

Summary

Returns every GNode within the input sequence that is not an ancestor of another member of the input sequence; the GNodes are returned in document order with duplicates eliminated.

Signature
fn:innermost(
$nodesas gnode()*
) as gnode()*
Properties

This function is deterministic, context-independent, and focus-independent.

Rules

The effect of the function call fn:innermost($nodes) is defined to be equivalent to the result of the expression:

$nodes except $nodes/ancestor::gnode()

That is, the function takes as input a sequence of GNodes, and returns every GNode within the sequence that is not an ancestor of another GNode within the sequence; the GNodes are returned in document order with duplicates eliminated.

Notes

If the supplied argument includes a map or an array, it will automatically be coerced to a JNode.

Examples
Expression:
parse-xml("<doc>
    <div id='a'><div id='b'><div id='c'/></div></div>
  </doc>")//div
   => innermost() ! string(@id)
parse-xml("<doc>
    <div id='a'><div id='b'><div id='c'/></div></div>
  </doc>")//div
   => innermost() => for-each(fn{string(@id)})
Result:
"c"
Expression:
[[[1,2], [3,4]], [[5,6], [7,8]]])//array(*)
   => innermost() =!> jnode-content()
[[[1,2], [3,4]], [[5,6], [7,8]]]//array(*)
   => innermost() =!> jnode-content()
Result:
[1,2], [3,4], [5,6], [7,8]

12.3.3 fn:outermost

Changes in 4.0  

  1. Generalized to work with JNodes as well as XNodes.  [Issue 2100 ]

Summary

Returns every GNode within the input sequence that has no ancestor that is itself a member of the input sequence; the nodes are returned in document order with duplicates eliminated.

Signature
fn:outermost(
$nodesas gnode()*
) as gnode()*
Properties

This function is deterministic, context-independent, and focus-independent.

Rules

The effect of the function call fn:outermost($nodes) is defined to be equivalent to the result of the expression:

$nodes[not(ancestor::gnode() intersect $nodes)]/.

That is, the function takes as input a sequence of GNodes, and returns every GNode within the sequence that does not have another GNode within the sequence as an ancestor; the GNodes are returned in document order with duplicates eliminated.

Notes

The formulation $nodes except $nodes/descendant::node() might appear to be simpler, but does not correctly account for attribute nodes, as these are not descendants of their parent element.

The motivation for the function was based on XSLT streaming use cases. There are cases where the [XSL Transformations (XSLT) Version 4.0] streaming rules allow the construct outermost(//section) but do not allow //section; the function can therefore be useful in cases where it is known that sections will not be nested, as well as cases where the application actually wishes to process all sections except those that are nested within another.

If the supplied argument includes a map or an array, it will automatically be coerced to a JNode.

Examples
Expression:
parse-xml("<doc>
     <div id='a'><div id='b'><div id='c'/></div></div>
  </doc>")//div
   => outermost() =!> string(@id)
parse-xml("<doc>
     <div id='a'><div id='b'><div id='c'/></div></div>
  </doc>")//div
   => outermost() => for-each(fn{string(@id)})
Result:
"a"
Expression:
[[[1], [2]], [[3], [4]], [[5], [6]]])//array(*)
   => outermost() =!> array:size()
[[[1], [2]], [[3], [4]], [[5], [6]]]//self::array(*)
   => outermost() =!> array:size()
Result:
3

14 Processing maps

Maps were introduced as a new datatype in XDM 3.1. This section describes functions that operate on maps.

A map is a kind of item.

[Definition] A map consists of a sequence of entries, also known as key-value pairs. Each entry comprises a key which is an arbitrary atomic item, and an arbitrary sequence called the associated value.

[Definition] Within a map, no two entries have the same key. Two atomic items K1 and K2 are the same key for this purpose if the function call fn:atomic-equal($K1, $K2) returns true.

It is not necessary that all the keys in a map should be of the same type (for example, they can include a mixture of integers and strings).

Maps are immutable, and have no identity separate from their content. For example, the map:remove function returns a map that differs from the supplied map by the omission (typically) of one entry, but the supplied map is not changed by the operation. Two calls on map:remove with the same arguments return maps that are indistinguishable from each other; there is no way of asking whether these are “the same map”.

A map can also be viewed as a function from keys to associated values. To achieve this, a map is also a function item. The function corresponding to the map has the signature function($key as xs:anyAtomicValue) as item()*. Calling the function has the same effect as calling the map:get function: the expression $map($key) returns the same result as get($map, $key). For example, if $books-by-isbn is a map whose keys are ISBNs and whose assocated values are book elements, then the expression $books-by-isbn("0470192747") returns the book element with the given ISBN. The fact that a map is a function item allows it to be passed as an argument to higher-order functions that expect a function item as one of their arguments.

14.5 Converting elements to maps

Changes in 4.0  

  1. A new function fn:element-to-map is provided for converting XDM trees to maps suitable for serialization as JSON. Unlike the fn:xml-to-json function retained from 3.1, this can handle arbitrary XML as input.   [Issues 528 1645 1646 1647 1648 1658 1658 1797 PRs 1575 1906 19 November 2024]

The fn:element-to-map function converts a tree rooted at an XML element node to a corresponding tree of maps, in a form suitable for serialization as JSON. In effect it provides a mechanism for converting XML to JSON.

This section describes the mappings used by this function.

This mapping is designed with three objectives:

  • It should be possible to represent any XML element as a map suitable for JSON serialization.

  • The resulting JSON should be intuitive and easy to use.

  • The JSON should be consistent and stable: small variations in the input should not result in large variations in the output.

Achieving all three objectives requires design compromises. It also requires sacrificing some other desiderata. In consequence:

  • The conversion is not lossless (see 14.5.8 Lost XDM Information for details).

  • The conversion is not streamable.

  • The results are not necessarily compatible with those produced by other popular libraries.

The requirement for consistency and stability is particularly challenging. An element such as <name>John</name> maps naturally to the map { "name": "John" }; but adding an attribute (so it becomes <name role="first">John</name>) then requires an incompatible change in the JSON representation. The format could be made extensible by converting <name>John</name> to { "name": {"#content":"John"} } and <name role="first">John</name> to { "name": { "@role":"first", "#content":"John" } }, but this imposes unwanted complexity on the simplest cases. The solution adopted is threefold:

  • It is possible to analyze a corpus of XML documents to develop a conversion plan, which can then be applied consistently to individual input documents, whether or not these documents were present in the corpus. The conversion plan can be serialized and subsequently reused, so that it can be applied to input documents that might not have existed at the time the conversion plan was formulated.

  • Alternatively, the function can make use of schema information where available, so it considers not just the structure of an individual element instance, but the rules governing the element type.

  • It is possible to override the choices made by the system, and explicitly specify the format to be used for elements or attributes having a given name.

14.5.11 fn:element-to-map

Changes in 4.0  

  1. New in 4.0.  [ PR 1906]

Summary

Converts an element node into a map that is suitable for JSON serialization.

Signature
fn:element-to-map(
$elementas element()?,
$optionsas map(*)?:= {}
) as map(xs:string, item()?)?
Properties

This function is deterministic, context-independent, and focus-independent.

Rules

This function returns a map derived from the element node supplied in $element. The map is in a form that is suitable for JSON serialization, thus providing a mechanism for conversion of arbitrary XML to JSON.

The map that is returned will always be a single-entry map; the key of this entry will be a string representing the element name, and the value of the entry will be a representation of the element's attributes and children.

The entries that may appear in the $options map are as follows. The option parameter conventions apply.

record(
plan?as map(xs:string, record(layout?, child?, type?, *)),
attribute-marker?as xs:string,
name-format?as xs:string
)
KeyValueMeaning

plan?

A conversion plan, supplied as a map whose keys represent element and attribute names. The plan might be generated using the function element-to-map-plan, or it might be constructed in some other way. The format of the plan is described in 14.5.2 Creating a conversion plan.
  • Type: map(xs:string, record(layout?, child?, type?, *))

  • Default: {}

attribute-marker?

A string that is prepended to any key value in the output that represents an XDM attribute node in the input. The string may be empty. If, after applying the requested prefix (or no prefix) there is a conflict between the names of attributes and child elements, then the requested prefix (or lack thereof) is ignored and the default prefix "@" is used.
  • Type: xs:string

  • Default: "@"

name-format?

Indicates how the names of element and attribute nodes are handled.
  • Type: xs:string

  • Default: "default"

lexicalNames are output in the form produced by the fn:name function.
localNames are output in the form produced by the fn:local-name function.
eqnameNames in a namespace are output in the form "Q{uri}local". Names in no namespace are output using the local name alone.
defaultAn element name is output as a local name alone if either (a) it is a top-level element and is in no namespace, or (b) it is in the same namespace as its parent element. An attribute name is output as a local name alone if it is in no namespace. All other names are output in the format "Q{uri}local" if in a namespace, or "Q{}local" if in no namespace. "Top-level" here means that the element is one that appears explicitly in the sequence of elements passed in the $elements argument, as distinct from a descendant of such an element.

If $element is an empty sequence, the result is an empty sequence.

The principles for conversion from elements to maps are described in 14.5.1 Element Layouts, and the rules for selecting an element layout for each element are given in 14.5.5 Selecting an element layout.

In general, every descendant element within the tree rooted at the supplied $element maps to a key-value pair in which the key represents the element name, and the corresponding value represents the attributes and children of the element. This key-value pair will be added to the content representing its parent element, in a way that depends on the parent element's layout.

The representation of a node of any other kind depends on the layout chosen for its parent element.

Error Conditions

A dynamic error [err:FOJS0008] occurs if any element cannot be processed using the selected layout for that element, unless fallback processing is defined; or if error action is explicitly requested for an element.

Any error in the conversion plan is treated as a type error [err:XPTY0004]XP whether or not it is technically a contravention of the defined type for the value. This relieves users and implementers of the burden of distinguishing different kinds of error in the plan.

Examples
Expression:

element-to-map(())

Result:

()

Expression:

element-to-map(<foo>bar</foo>)

Result:

{ "foo": "bar" }

Expression:
element-to-map(
    <list>
      <item value='1'/>
      <item value='2'/>
    </list>, { 'attribute-marker': '' }
  )
Result:
{ "list": [ 
    { "value": "1" },
    { "value": "2" }
  ] }
Expression:
element-to-map(
    <name>
      <first>Jane</first>
      <last>Smith</last>
    </name>
  )
Result:
{ "name": { 
  "first": "Jane",
  "last": "Smith" 
} }
Expression:
element-to-map(
    <name xmlns="http://example.ns">
      <first>Jane</first>
      <middle>Elizabeth</middle>
      <middle>Mary</middle>
      <last>Smith</last>
    </name>, 
    { 'plan': {'name': { 'layout': 'record' }},
      'name-format' : 'local'
    }
  )
Result:
{ "name": { 
    "first": "Jane",
    "middle": ["Elizabeth", "Mary"]
    "last": "Smith" 
  } 
}
{ "name": { 
    "first": "Jane",
    "middle": ["Elizabeth", "Mary"],
    "last": "Smith" 
  } 
}
Expression:
element-to-map(
    <name xmlns="http://example.ns">
      <first>Jane</first>
      <middle>Elizabeth</middle>
      <middle>Mary</middle>
      <last>Smith</last>
    </name>, 
    { 'plan': {'name': { 'layout': 'record' },
               'middle': { 'layout': 'deep-skip' }
              },
      'name-format' : 'local'
    }
  )
Result:
{ "name": { 
    "first": "Jane",
    "last": "Smith" 
  } 
}

16 Processing JNodes

Changes in 4.0  

  1. Introduced the concept of JNodes.   [Issue 2025 PR 2031 11 June 2025]

A JNodeDM is a wrapper around a map or array, or around a value that appears within the content of a map or array. JNodes are described at [XQuery and XPath Data Model (XDM) 4.0] section 8.4 JNodes. Wrapping a map or array in a JNode enables the use of path expressions such as $jnode/descendant::title, as described at [XML Path Language (XPath) 4.0] section 4.7 Path Expressions.

In addition to the functions defined in this section, functions that operate on JNodes include:

fn:distinct-ordered-nodes
fn:generate-id
fn:has-children
fn:innermost
fn:outermost
fn:path
fn:root
fn:siblings
fn:transitive-closure

16.1 Functions on JNodes

16.1.2 fn:jnode-content

Summary

Returns the ·content· property of a JNode.

Signature
fn:jnode-content(
$inputas jnode()?:= .
) as item()*
Properties

This function is deterministic, context-independent, and focus-independent.

Rules

If the argument is omitted, it defaults to the context value (.).

If $input is an empty sequence, the function returns an empty sequence.

Otherwise, the function returns the ·content· property of $input.

Error Conditions

The following errors may be raised when $node is omitted:

  • If the context value is absentDM, type error [err:XPDY0002]XP.

  • If the context value is not an instance of the sequence type jnode()?, type error [err:XPTY0004]XP.

Notes

In many cases it is unnecessary to make an explicit call on jnode-content, because the coercion rules will take care of this automatically. For example, in an expression such as $X / descendant::name [matches(., '^J')], the call on matches is supplied with a JNode as its first argument; atomization ensures that the actual value being passed to the first argument of matches is the atomized value of the ·content· property.

Other examples where the ·content· of a JNode is extracted automatically include:

  • Any context where the required type is an atomic value, for example arithmetic operations, value comparisons and general comparisons, and calls on functions that expect an atomic value.

  • Any context where the required type is a map or array, for example the first argument of functions such as map:size or array:size, a free-standing expression within a map constructor such as map{ $jnode }, the constructs for member and for key/value, the left-hand operand of the lookup operator ? (or the context value in the case of a unary lookup operator), and the operand of a map/array filter expression $jnode?[predicate].

Notable places where the ·content· is not automatically extracted include:

  • When computing the effective boolean value. As with XNodes, writing if ($array/child::*[1]) ... tests for the existence of a child, it does not test its value. To test its value, write if (jnode-content($array/child::*[1])) ..., or equivalently if (xs:boolean($array/child::*[1])) ....

  • When calling functions that accept arbitrary sequences, such as count or deep-equal.

It is possible (though probably unwise) to construct a JNode whose ·content· property itself contains another JNode. For example, the expression jtree([jtree([]), jtree([])]) creates a JNode whose ·content· is an array of JNodes, and applying the child axis to this JNode will return a sequence of two JNodes that themselves have further JNodes as their content. The jnode-content returns these contained JNodes, it does not recursively extract their content.

Examples
Expression:
let $array := [1, 3, 4.5, 7, "eight", 10]
return $array / child::type(xs:integer) =!> jnode-content()
Result:
1, 3, 7, 10
Expression:
let $map := {'Mo': 'Monday', 'Tu': 'Tuesday', 'We': 'Wednesday'}
return $map / child::get("Mo", "We", "Fr", "Su") =!> jnode-content()
Result:
"Monday", "Wednesday"
Expression:
let $array := [[4, 18], [30, 4, 22]]
return $array / descendant::*[. gt 25][1] / ancestor-or-self::* =!> jnode-content() => reverse()
let $array := [[4, 18], [30, 4, 22]]
return $array / descendant::*[. > 25][1] / ancestor-or-self::* =!> jnode-content()
Result:
[[4, 18], [30, 4, 22]], [30, 4, 22], 30
[[4, 18], [30, 4, 22]], [30, 4, 22]

16.1.3 fn:jnode-selector

Changes in 4.0  

  1. New in 4.0  [Issue 2025 PR 2031 12 June 2025]

Summary

Returns the ·selector· property of a JNode.

Signature
fn:jnode-selector(
$inputas jnode()?:= .
) as xs:anyAtomicType?
Properties

This function is deterministic, context-independent, and focus-independent.

Rules

If the argument is omitted, it defaults to the context value (.).

If $input is an empty sequence, the function returns an empty sequence.

If $input is a root JNode (one in which the ·selector· property is absent), the function returns an empty sequence.

Otherwise, the function returns the ·selector· property of $input. In the case where the parent JNode wraps a map, this will be the key of the relevant entry within that map; in the case where the parent JNode wraps an array, it will be the 1-based index of the relevant member of the array.

Error Conditions

The following errors may be raised when $node is omitted:

  • If the context value is absentDM, type error [err:XPDY0002]XP.

  • If the context value is not an instance of the sequence type jnode()?, type error [err:XPTY0004]XP.

Examples
Expression:
let $array := [1, 3, 4.5, 7, "eight", 10]
return $array / child::type(xs:integer) =!> jnode-selector()
Result:
1, 2, 4, 6
Expression:
let $map := {'Mo': 'Monday', 'Tu': 'Tuesday', 'We': 'Wednesday'}
return $map / child::get("Mo", "We", "Fr", "Su") =!> jnode-selector()
Result:
"Mo", "We"
Expression:
let $array := [[4, 18], [30, 4, 22]]
return $array / descendant::*[. > 25][1] / ancestor::* =!> jnode-selector() => reverse()
let $array := [[4, 18], [30, 4, 22]]
return $array / descendant::*[. > 25] / ancestor-or-self::* =!> jnode-selector()
Result:
2, 1

16.1.4 fn:jnode-position

Changes in 4.0  

  1. New in 4.0  [Issue 2025 PR 2031 12 June 2025]

Summary

Returns the ·position· property of a JNode.

Signature
fn:jnode-position(
$inputas jnode()?:= .
) as xs:anyAtomicType?
Properties

This function is deterministic, context-independent, and focus-independent.

Rules

If the argument is omitted, it defaults to the context value (.).

If $input is an empty sequence, the function returns an empty sequence.

If $input is a root JNode (one in which the ·position· property is absent), the function returns an empty sequence.

Otherwise, the function returns the ·position· property of $input. The value of this property will be 1 (one) except in cases where the value of an entry in a map, or a member in an array, is a sequence that contains multiple items including maps and/or arrays; in such cases the position will be the 1-based position of the relevant map or array.

Error Conditions

The following errors may be raised when $node is omitted:

  • If the context value is absentDM, type error [err:XPDY0002]XP.

  • If the context value is not an instance of the sequence type jnode()?, type error [err:XPTY0004]XP.

Notes

This function is relevant only when there are maps whose entries are multi-item sequences that include maps and arrays, or arrays whose members include such multi-item sequences. Such structures are uncommon, and never arise from parsing of JSON source text. It is generally best to avoid such structures by using arrays rather than sequences within array and map content; apart from other considerations, this allows the data to be serialized in JSON format.

If an entry within a map, or a member of an array, contains a sequence of items that mixes arrays and maps with other content (for example the array [1, 2, ([1,2], [3,4], 5)), then a lookup using the child axis will only construct JNodes in respect of those items that are non-empty maps or arrays. This may leave gaps in the position numbering sequence, as illustrated in the examples below.

Examples
Expression:
let $input := {
   "a": [10, 20, 30], 
   "b": ([40, 50, 60], [], 0, [70, 80, (90, 100)])
}
return $input / child::b / * 
       ! { "position": jnode-position(),
           "index": jnode-selector()
           "value": jnode-content()
         }
let $input := {
   "a": [10, 20, 30], 
   "b": ([40, 50, 60], [], 0, [70, 80, (90, 100)])
}
return $input / child::b / * 
       ! { "position": jnode-position(),
           "index": jnode-selector(),
           "value": jnode-content()
         }
Result:
{ "position": 1, "index": 1, "value": 40 },
{ "position": 1, "index": 2, "value": 50 },
{ "position": 1, "index": 3, "value": 60 },
{ "position": 4, "index": 1, "value": 70 },
{ "position": 4, "index": 2, "value": 80 },
{ "position": 4, "index": 3, "value": (90, 100) }
Expression:
let $input := {
   "a": {"x": 10, "y": 20, "z": 30}, 
   "b": ( {"x": 40, "y": 50, "z": 60},
          {},
          {"x": 70, "y": 80, "z": (90, 100)})
}
return $input / child::b / * 
       ! { "position": jnode-position(),
           "key": jnode-selector()
           "value": jnode-content()
         }
let $input := {
   "a": {"x": 10, "y": 20, "z": 30}, 
   "b": ( {"x": 40, "y": 50, "z": 60},
          {},
          {"x": 70, "y": 80, "z": (90, 100)})
}
return $input / child::b / * 
       ! { "position": jnode-position(),
           "key": jnode-selector(),
           "value": jnode-content()
         }
Result:
{ "position": 1, "key": "x", "value": 40 },
{ "position": 1, "key": "y", "value": 50 },
{ "position": 1, "key": "z", "value": 60 },
{ "position": 3, "key": "x", "value": 70 },
{ "position": 3, "key": "y", "value": 80 },
{ "position": 3, "key": "z", "value": (90, 100) }