Maps and arrays are introduced as new datatypes in XDM 3.1. This section describes functions that operate on maps and arrays. It also describes functions that operate on JSON data structures, which make extensive use of maps and arrays.
The functions defined in this section use a conventional namespace prefix map
, which
is assumed to be bound to the namespace URI http://www.w3.org/2005/xpath-functions/map
.
A map is an additional kind of item.
[Definition] A map consists of a set of entries. Each entry comprises a key which is an arbitrary atomic value, and an arbitrary sequence called the associated value.
[Definition] Within a map, no two entries have the same key.
Two atomic values K1
and K2
are the same key
for this purpose if the (internal) function call op:same-key($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).
As with all other values, the functions in this specification treat maps as immutable.
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".
The function call map:get($map, $key)
can be used to retrieve the value associated with a given key.
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 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.
There is no operation to atomize a map or convert it to a string. The function fn:serialize
can in some cases
be used to produce a JSON representation of a map.
Function | Meaning |
---|---|
op:same-key |
Determines whether two atomic values can coexist as separate keys within a map. |
map:merge |
Returns a map that combines the entries from a number of existing maps. |
map:size |
Returns the number of entries in the supplied map. |
map:keys |
Returns a sequence containing all the keys present in a map |
map:contains |
Tests whether a supplied map contains an entry for a given key |
map:get |
Returns the value associated with a supplied key in a given map. |
map:find |
Searches the supplied input sequence and any contained maps and arrays for a map entry with the supplied key, and returns the corresponding values. |
map:put |
Returns a map containing all the contents of the supplied map, but with an additional entry, which replaces any existing entry for the same key. |
map:entry |
Returns a map that contains a single entry (a key-value pair). |
map:remove |
Returns a map containing all the entries from a supplied map, except those having a specified key. |
map:filter |
Selects entries from a map, returning a new map. |
map:for-each |
Applies a supplied function to every entry in a map, returning the concatenation of the results. |
map:substitute |
Applies a supplied function to every entry in a map, returning a map whose entries have the same keys as the input, but (potentially) different associated values. |
map:replace |
Returns a map based on the contents of an existing map, computing a new value to be associated with a supplied key. |
map:build |
Returns a map that typically contains one entry for each item in a supplied input sequence. |
Determines whether two atomic values can coexist as separate keys within a map.
op:same-key ( |
||
$k1 |
as xs:anyAtomicType , |
|
$k2 |
as xs:anyAtomicType |
|
) as xs:boolean |
This function is ·deterministic·, ·context-independent·, and ·focus-independent·.
The internal function op:same-key
(which is not available at the user level) is used to assess whether two atomic
values are considered to be duplicates when used as keys in a map. A map cannot
contain two separate entries whose keys are the same as defined by this function.
The function is also used when matching keys in functions such as map:get
and map:remove
.
The function returns true if and only if one of the following conditions is true:
All of the following conditions are true:
$k1
is an instance of xs:string
, xs:anyURI
, or xs:untypedAtomic
$k2
is an instance of xs:string
, xs:anyURI
, or xs:untypedAtomic
fn:codepoint-equal($k1, $k2)
Note:
Strings are compared without any dependency on collations.
All of the following conditions are true:
$k1
is an instance of xs:decimal
, xs:double
, or xs:float
$k2
is an instance of xs:decimal
, xs:double
, or xs:float
One of the following conditions is true:
Both $k1
and $k2
are NaN
Note:
xs:double('NaN')
is the same key as xs:float('NaN')
Both $k1
and $k2
are positive infinity
Note:
xs:double('INF')
is the same key as xs:float('INF')
Both $k1
and $k2
are negative infinity
Note:
xs:double('-INF')
is the same key as xs:float('-INF')
$k1
and $k2
when converted to decimal numbers with no rounding or loss of precision
are mathematically equal.
Note:
Every instance of xs:double
, xs:float
, and xs:decimal
can be represented
exactly as a decimal number provided enough digits are available both before and after the decimal point. Unlike the eq
relation, which converts both operands to xs:double
values, possibly losing precision in the process, this
comparison is transitive.
Note:
Positive and negative zero are the same key.
All of the following conditions are true:
$k1
is an instance of xs:date
, xs:time
, xs:dateTime
,
xs:gYear
, xs:gYearMonth
, xs:gMonth
, xs:gMonthDay
, or xs:gDay
$k2
is an instance of xs:date
, xs:time
, xs:dateTime
,
xs:gYear
, xs:gYearMonth
, xs:gMonth
, xs:gMonthDay
, or xs:gDay
One of the following conditions is true:
Both $k1
and $k2
have a timezone
Neither $k1
nor $k2
has a timezone
fn:deep-equal($k1, $k2)
Note:
The use of deep-equal
rather than eq
ensures that comparing values of different
types yields false
rather than an error.
Note:
Unlike the eq
operator, this comparison has no dependency on the implicit timezone, which means that
the question of whether or not a map contains duplicate keys is not dependent on this aspect of the dynamic context.
All of the following conditions are true:
$k1
is an instance of xs:boolean
, xs:hexBinary
, xs:base64Binary
,
xs:duration
, xs:QName
, or xs:NOTATION
$k2
is an instance of xs:boolean
, xs:hexBinary
, xs:base64Binary
,
xs:duration
, xs:QName
, or xs:NOTATION
fn:deep-equal($k1, $k2)
Note:
The use of deep-equal
rather than eq
ensures that comparing values of different
types yields false
rather than an error.
The rules for comparing keys in a map are chosen to ensure that the comparison is:
Context-free: there is no dependency on the static or dynamic context
Error-free: any two atomic values can be compared, and the result is either true or false, never an error
Transitive: if A is the same key as B, and B is the same key as C, then A is the same key as C.
As always, any algorithm that delivers the right result is acceptable. For example, when testing whether an xs:double
value D is the same key as an xs:decimal
value that has N significant digits, it is not
necessary to know all the digits in the decimal expansion of D to establish the result: computing the first N+1
significant digits (or indeed, simply knowing that there are more than N significant digits) is sufficient.
Returns a map that combines the entries from a number of existing maps.
map:merge ( |
||
$maps |
as map(*)* |
|
) as map(*) |
map:merge ( |
||
$maps |
as map(*)* , |
|
$options |
as map(*) |
|
) as map(*) |
This function is ·deterministic·, ·context-independent·, and ·focus-independent·.
The function map:merge
returns a map that
is formed by combining the contents of the maps supplied in the $maps
argument.
Informally, the supplied maps are combined as follows:
There is one entry in the returned map for each distinct key present in the union of the input maps, where two keys are distinct if they are not the ·same key·.
If there are duplicate keys, that is, if two or more maps contain entries having the
·same key·, then the way this is handled is
controlled by the second ($options
) argument.
The definitive specification is as follows.
The effect of calling the single-argument function is the same as the effect of
calling the two-argument function with an empty map as the value of $options
.
The $options
argument can be used to control the way in which duplicate keys are handled.
The ·option parameter conventions· apply.
The entries that may appear in the $options
map are as follows:
Key | Value | Meaning |
---|---|---|
duplicates |
Determines the policy for handling duplicate keys: specifically, the action to be
taken if two maps in the input sequence $maps contain entries with key values
K1 and K2 where K1 and K2 are the
·same key·.
|
|
reject |
An error is raised [err:FOJS0003] if duplicate keys are encountered. | |
use-first |
If duplicate keys are present, all but the first of a set of duplicates are ignored,
where the ordering is based on the order of maps in the $maps argument.
|
|
use-last |
If duplicate keys are present, all but the last of a set of duplicates are ignored,
where the ordering is based on the order of maps in the $maps argument.
|
|
use-any |
If duplicate keys are present, all but one of a set of duplicates are ignored, and it is ·implementation-dependent· which one is retained. | |
combine |
If duplicate keys are present, the result map includes an entry for the key whose
associated value is the sequence-concatenation of all the values associated with the
key,
retaining order based on the order of maps in the $maps argument.
The key value in the result map that corresponds to such a set of duplicates must
be the ·same key· as each of the duplicates, but it is
otherwise unconstrained: for example if the duplicate keys are xs:byte(1)
and xs:short(1) , the key in the result could legitimately be xs:long(1) .
|
The result of the function call map:merge($MAPS, $OPTIONS)
is defined to be consistent with the result of the expression:
let $FOJS0003 := QName("http://www.w3.org/2005/xqt-errors", "FOJS0003"), $duplicates-handler := map { "use-first": function($a, $b) {$a}, "use-last": function($a, $b) {$b}, "combine": function($a, $b) {$a, $b}, "reject": function($a, $b) {fn:error($FOJS0003)}, "use-any": function($a, $b) {fn:random-number-generator()?permute(($a, $b))[1]} }, $combine-maps := function($A as map(*), $B as map(*), $deduplicator as function(*)) { fn:fold-left(map:keys($B), $A, function($z, $k){ if (map:contains($z, $k)) then map:put($z, $k, $deduplicator($z($k), $B($k))) else map:put($z, $k, $B($k)) }) } return fn:fold-left($MAPS, map{}, $combine-maps(?, ?, $duplicates-handler(($OPTIONS?duplicates, "use-first")[1]))
Note:
By way of explanation, $combine-maps
is a function that combines
two maps by iterating over the keys of the second map, adding each key and its corresponding
value to the first map as it proceeds. The second call of fn:fold-left
in the return
clause then iterates over the maps supplied in the call
to map:merge
, accumulating a single map that absorbs successive maps
in the input sequence by calling $combine-maps
.
This algorithm processes the supplied maps in a defined order, but processes the keys within each map in implementation-dependent order.
The use of fn:random-number-generator
represents one possible conformant
implementation for "duplicates":"use-any"
, but it is not the only conformant
implementation and is not intended to be a realistic implementation. The purpose of this
option is to allow the implementation to use whatever strategy is most efficient; for example,
if the input maps are processed in parallel, then specifying "duplicates":"use-any"
means that the implementation does not need to keep track of the original order of the sequence of input
maps.
An error is raised [err:FOJS0003] if the value of
$options
indicates that duplicates are to be rejected, and a duplicate key is encountered.
An 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.
If the input is an empty sequence, the result is an empty map.
If the input is a sequence of length one, the result map is indistinguishable from the supplied map.
There is no requirement that the supplied input maps should have the same or compatible
types. The type of a map (for example map(xs:integer, xs:string)
) is
descriptive of the entries it currently contains, but is not a constraint on how the map
may be combined with other maps.
let $week := map{0:"Sonntag", 1:"Montag", 2:"Dienstag", 3:"Mittwoch", 4:"Donnerstag", 5:"Freitag", 6:"Samstag"}
The expression map:merge(())
returns map{}
. (Returns an empty map).
The expression map:merge((map:entry(0, "no"), map:entry(1, "yes")))
returns map{0:"no", 1:"yes"}
. (Returns a map with two entries).
The expression map:merge(($week, map{7:"Unbekannt"}))
returns map{0:"Sonntag", 1:"Montag", 2:"Dienstag", 3:"Mittwoch", 4:"Donnerstag",
5:"Freitag", 6:"Samstag", 7:"Unbekannt"}
. (The value of the existing map is unchanged; the returned map
contains all the entries from $week
, supplemented with an additional
entry.)
The expression map:merge(($week, map{6:"Sonnabend"}), map{"duplicates":"use-last"})
returns map{0:"Sonntag", 1:"Montag", 2:"Dienstag", 3:"Mittwoch", 4:"Donnerstag",
5:"Freitag", 6:"Sonnabend"}
. (The value of the existing map is unchanged; the returned map
contains all the entries from $week
, with one entry replaced by a
new entry. Both input maps contain an entry with the key 6
; the
one used in the result is the one that comes last in the input
sequence.)
The expression map:merge(($week, map{6:"Sonnabend"}), map{"duplicates":"use-first"})
returns map{0:"Sonntag", 1:"Montag", 2:"Dienstag", 3:"Mittwoch", 4:"Donnerstag",
5:"Freitag", 6:"Samstag"}
. (The value of the existing map is unchanged; the returned map
contains all the entries from $week
, with one entry replaced by a
new entry. Both input maps contain an entry with the key 6
; the
one used in the result is the one that comes first in the input
sequence.)
The expression map:merge(($week, map{6:"Sonnabend"}), map{"duplicates":"combine"})
returns map{0:"Sonntag", 1:"Montag", 2:"Dienstag", 3:"Mittwoch", 4:"Donnerstag",
5:"Freitag", 6:("Samstag", "Sonnabend")}
. (The value of the existing map is unchanged; the returned map
contains all the entries from $week
, with one entry replaced by a
new entry. Both input maps contain an entry with the key 6
; the
entry that appears in the result is the sequence-concatenation of the entries
in the input maps, retaining order.)
Returns the number of entries in the supplied map.
map:size ( |
||
$map |
as map(*) |
|
) as xs:integer |
This function is ·deterministic·, ·context-independent·, and ·focus-independent·.
The function map:size
takes any ·map·
as its $map
argument and returns the number of entries that are present
in the map.
The expression map:size(map{})
returns 0
.
The expression map:size(map{"true":1, "false":0})
returns 2
.
Returns a sequence containing all the keys present in a map
map:keys ( |
||
$map |
as map(*) |
|
) as xs:anyAtomicType* |
This function is ·nondeterministic-wrt-ordering·, ·context-independent·, and ·focus-independent·.
The function map:keys
takes any ·map·
as its $map
argument and returns the keys that are present in the map as
a sequence of atomic values, in ·implementation-dependent· order.
The function is non-deterministic with respect to ordering (see 1.7.4 Properties of functions). This means that two calls with the same argument are not guaranteed to produce the results in the same order.
The number of items in the result will be the same as the number of entries in the map, and the result sequence will contain no duplicate values.
The expression map:keys(map{1:"yes", 2:"no"})
returns some permutation of (1,2)
. (The result is in ·implementation-dependent· order.)
Tests whether a supplied map contains an entry for a given key
map:contains ( |
||
$map |
as map(*) , |
|
$key |
as xs:anyAtomicType |
|
) as xs:boolean |
This function is ·deterministic·, ·context-independent·, and ·focus-independent·.
The function map:contains
returns true if the ·map· supplied as $map
contains an entry with the ·same key· as $key
; otherwise it returns false.
let $week := map{0:"Sonntag", 1:"Montag", 2:"Dienstag", 3:"Mittwoch", 4:"Donnerstag", 5:"Freitag", 6:"Samstag"}
The expression map:contains($week, 2)
returns true()
.
The expression map:contains($week, 9)
returns false()
.
The expression map:contains(map{}, "xyz")
returns false()
.
The expression map:contains(map{"xyz":23}, "xyz")
returns true()
.
The expression map:contains(map{"abc":23, "xyz":()}, "xyz")
returns true()
.
Returns the value associated with a supplied key in a given map.
map:get ( |
||
$map |
as map(*) , |
|
$key |
as xs:anyAtomicType |
|
) as item()* |
This function is ·deterministic·, ·context-independent·, and ·focus-independent·.
The function map:get
attempts to find an entry within the ·map· supplied as $map
that has
the ·same key· as $key
. If there is such an entry, it returns the associated value;
otherwise it returns an empty sequence.
A return value of ()
from map:get
could indicate that
the key is present in the map with an associated value of ()
, or it could
indicate that the key is not present in the map. The two cases can be distinguished by
calling map:contains
.
Invoking the ·map· as a function item has the same effect
as calling get
: that is, when $map
is a map, the expression
$map($K)
is equivalent to map:get($map, $K)
. Similarly, the
expression map:get(map:get(map:get($map, 'employee'), 'name'), 'first')
can
be written as $map('employee')('name')('first')
.
let $week := map{0:"Sonntag", 1:"Montag", 2:"Dienstag", 3:"Mittwoch", 4:"Donnerstag", 5:"Freitag", 6:"Samstag"}
The expression map:get($week, 4)
returns "Donnerstag"
.
The expression map:get($week, 9)
returns ()
. (When the key is not present, the function returns an empty
sequence.)
The expression map:get(map:entry(7,()), 7)
returns ()
. (An empty sequence as the result can also signify that the key is
present and the associated value is an empty sequence.)
Searches the supplied input sequence and any contained maps and arrays for a map entry with the supplied key, and returns the corresponding values.
map:find ( |
||
$input |
as item()* , |
|
$key |
as xs:anyAtomicType |
|
) as array(*) |
This function is ·deterministic·, ·context-independent·, and ·focus-independent·.
The function map:find
searches the sequence supplied as $input
looking for map entries whose key is the ·same key·
as $key
. The associated value in any such map entry (each being in general a sequence)
is returned as a member of the result array.
The search processes the $input
sequence using the following recursively defined rules
(any equivalent algorithm may be used provided it delivers
the same result, respecting those rules that constrain the order of the result):
To process a sequence, process each of its items in order.
To process an item that is an array, process each of the array's members in order (each member is, in general, a sequence).
To process an item that is a map, then for each key-value entry (K, V) in the map (in ·implementation-dependent· order) perform both of the following steps, in order:
If K is the ·same key· as $key
,
then add V as a new member to the end of the result array.
Process V (which is, in general, a sequence).
To process an item that is neither a map nor an array, do nothing. (Such items are ignored).
If $input
is an empty sequence, map, or array, or if the requested $key
is not found,
the result will be a zero-length array.
let $responses := [map{0:'no', 1:'yes'}, map{0:'non', 1:'oui'}, map{0:'nein', 1:('ja', 'doch')}]
The expression map:find($responses, 0)
returns ['no', 'non', 'nein']
.
The expression map:find($responses, 1)
returns ['yes', 'oui', ('ja', 'doch')]
.
The expression map:find($responses, 2)
returns []
.
let $inventory := map{"name":"car", "id":"QZ123", "parts": [map{"name":"engine", "id":"YW678", "parts":[]}]}
The expression map:find($inventory, "parts")
returns [[map{"name":"engine", "id":"YW678", "parts":[]}], []]
.
Returns a map containing all the contents of the supplied map, but with an additional entry, which replaces any existing entry for the same key.
map:put ( |
||
$map |
as map(*) , |
|
$key |
as xs:anyAtomicType , |
|
$value |
as item()* |
|
) as map(*) |
This function is ·deterministic·, ·context-independent·, and ·focus-independent·.
The function map:put
returns a ·map· that contains all entries from the supplied $map
,
with the exception of any entry whose key is the ·same key· as $key
, together with a new
entry whose key is $key
and whose associated value is $value
.
The effect of the function call map:put($MAP, $KEY, $VALUE)
is equivalent
to the result of the following steps:
let $MAP2 := map:remove($MAP, $KEY)
This returns a map in which all entries with the same key as $KEY
have been removed.
Construct and return a map containing:
All the entries (key/value pairs) in $MAP2
, and
The entry map:entry($KEY, $VALUE)
There is no requirement that the type of $key
and $value
be consistent with the types
of any existing keys and values in the supplied map.
let $week := map{0:"Sonntag", 1:"Montag", 2:"Dienstag", 3:"Mittwoch", 4:"Donnerstag", 5:"Freitag", 6:"Samstag"}
The expression map:put($week, 6, "Sonnabend")
returns map{0:"Sonntag", 1:"Montag", 2:"Dienstag", 3:"Mittwoch", 4:"Donnerstag",
5:"Freitag", 6:"Sonnabend"}
.
The expression map:put($week, -1, "Unbekannt")
returns map{0:"Sonntag", 1:"Montag", 2:"Dienstag", 3:"Mittwoch", 4:"Donnerstag",
5:"Freitag", 6:"Samstag", -1:"Unbekannt"}
.
Returns a map that contains a single entry (a key-value pair).
map:entry ( |
||
$key |
as xs:anyAtomicType , |
|
$value |
as item()* |
|
) as map(*) |
This function is ·deterministic·, ·context-independent·, and ·focus-independent·.
The function map:entry
returns a ·map· which contains a single
entry. The key of the entry in the new map is
$key
, and its associated value is $value
.
The function call map:entry(K, V)
produces the same result as the
expression map{K : V}
.
The function map:entry
is intended primarily for use in conjunction with
the function map:merge
. For example, a map containing seven entries may be
constructed like this:
map:merge(( map:entry("Su", "Sunday"), map:entry("Mo", "Monday"), map:entry("Tu", "Tuesday"), map:entry("We", "Wednesday"), map:entry("Th", "Thursday"), map:entry("Fr", "Friday"), map:entry("Sa", "Saturday") ))
The map:merge
function can be used to construct
a map with a variable number of entries, for example:
map:merge(for $b in //book return map:entry($b/isbn, $b))
The expression map:entry("M", "Monday")
returns map{"M":"Monday"}
.
Returns a map containing all the entries from a supplied map, except those having a specified key.
map:remove ( |
||
$map |
as map(*) , |
|
$keys |
as xs:anyAtomicType* |
|
) as map(*) |
This function is ·deterministic·, ·context-independent·, and ·focus-independent·.
The function map:remove
returns a ·map· containing all the entries in $map
except for any entry whose key is
the ·same key· as an item in
$keys
.
No failure occurs if an item in $keys
does not correspond to any entry in $map
;
that key value is simply ignored.
The effect of the function call map:remove($MAP, $KEY)
can be described more formally as the result of the expression below:
map:merge ( map:for-each ( $MAP, -> $k, $v { if (some $key in $KEY satisfies (op:same-key($k, $key)) then () else map:entry($k, $v) } ) )
let $week := map{0:"Sonntag", 1:"Montag", 2:"Dienstag", 3:"Mittwoch", 4:"Donnerstag", 5:"Freitag", 6:"Samstag"}
The expression map:remove($week, 4)
returns map{0:"Sonntag", 1:"Montag", 2:"Dienstag", 3:"Mittwoch", 5:"Freitag",
6:"Samstag"}
.
The expression map:remove($week, 23)
returns map{0:"Sonntag", 1:"Montag", 2:"Dienstag", 3:"Mittwoch", 4:"Donnerstag",
5:"Freitag", 6:"Samstag"}
.
The expression map:remove($week, (0, 6 to 7))
returns map{1:"Montag", 2:"Dienstag", 3:"Mittwoch", 4:"Donnerstag", 5:"Freitag"}
.
The expression map:remove($week, ())
returns map{0:"Sonntag", 1:"Montag", 2:"Dienstag", 3:"Mittwoch", 4:"Donnerstag", 5:"Freitag",
6:"Samstag"}
.
Selects entries from a map, returning a new map.
map:filter ( |
||
$map |
as map(*) , |
|
$predicate |
as function(xs:anyAtomicType, item()*) as map(*) |
|
) as item()* |
This function is ·context-independent·, ·focus-independent·, and ·higher-order·.
The function map:filter
takes any ·map· as its $map
argument and applies the supplied function
to each entry in the map; the result is a new map containing those entries for which
the function returns true.
The function supplied as $predicate
takes two arguments. It is called
supplying the key of the map entry as the first argument, and the associated value as
the second argument.
The expression map:filter(map{1:"Sunday", 2:"Monday", 3:"Tuesday", 4:"Wednesday",
"5: "Thursday", 6:"Friday", 7:"Saturday"}, ->($k,
$v){$k = (1, 7)})
returns map:filter(map{1:"Sunday", 7:"Saturday"}
.
The expression map:filter(map{1:"Sunday", 2:"Monday", 3:"Tuesday", 4:"Wednesday",
"5: "Thursday", 6:"Friday", 7:"Saturday"}, ->($k,
$v){$v = ("Saturday", "Sunday")})
returns map:filter(map{1:"Sunday", 7:"Saturday"}
.
Proposed for 4.0.
Discussed 2022-09-20; decided to defer acceptance pending ideas for alignment with array:filter
.
Applies a supplied function to every entry in a map, returning the concatenation of the results.
map:for-each ( |
||
$map |
as map(*) , |
|
$action |
as function(xs:anyAtomicType, item()*) as item()* |
|
) as item()* |
This function is ·nondeterministic-wrt-ordering·, ·context-independent·, ·focus-independent·, and ·higher-order·.
The function map:for-each
takes any ·map· as its $map
argument and applies the supplied function
to each entry in the map, in ·implementation-dependent· order; the result is the sequence obtained by
concatenating the results of these function calls.
The function is non-deterministic with respect to ordering (see 1.7.4 Properties of functions). This means that two calls with the same arguments are not guaranteed to process the map entries in the same order.
The function supplied as $action
takes two arguments. It is called
supplying the key of the map entry as the first argument, and the associated value as
the second argument.
The expression map:for-each(map{1:"yes", 2:"no"}, function($k,
$v){$k})
returns some permutation of (1,2)
. (This function call is equivalent to calling map:keys
. The
result is in implementation-dependent order.)
The expression distinct-values(map:for-each(map{1:"yes", 2:"no"}, function($k,
$v){$v}))
returns some permutation of ("yes", "no")
. (This function call returns the distinct values present in the map, in
implementation-dependent order.)
The expression map:merge(map:for-each(map{"a":1, "b":2}, function($k,
$v){map:entry($k, $v+1)}))
returns map{"a":2, "b":3}
. (This function call returns a map with the same keys as the input map,
with the value of each entry increased by one.)
This XQuery example converts the entries in a map to attributes on a newly constructed element node:
let $dimensions := map{'height': 3, 'width': 4, 'depth': 5}; return <box>{ map:for-each($dimensions, function ($k, $v) { attribute {$k} {$v} }) }</box>
The result is the element <box height="3" width="4"
depth="5"/>
.
Applies a supplied function to every entry in a map, returning a map whose entries have the same keys as the input, but (potentially) different associated values.
map:substitute ( |
||
$map |
as map(*) , |
|
$action |
as function(xs:anyAtomicType, item()*) as item()* |
|
) as map(*) |
This function is ·context-independent·, ·focus-independent·, and ·higher-order·.
The function map:substitute
takes any ·map· as its $map
argument and applies the supplied function
to each entry in the map; the result is a map that associates the original set of key
values with the results of these function calls.
The function supplied as $action
takes two arguments. It is called
supplying the key of the map entry as the first argument, and the associated value as
the second argument.
The function call map:substitute($m, $f)
returns the same result as
map:merge(map:for-each($m, -> ($k, $v) { map:entry($k, $f($k, $v)) }))
.
The expression map:substitute(map{1:true(), 2:false()}, function($k,
$v){not($v)})
returns map{1:false(), 2:true()}
.
The expression map:substitute(map{1:"yes", 2:"no"}, function($k,
$v){$v || ' (' || $k || ')'}))
returns map{1:"yes (1)", 2:"no (2)"}
.
Returns a map based on the contents of an existing map, computing a new value to be associated with a supplied key.
map:replace ( |
||
$map |
as map(*) , |
|
$key |
as xs:anyAtomicType , |
|
$action |
as function(item()*) as item()* |
|
) as map(*) |
This function is ·context-independent·, ·focus-independent·, and ·higher-order·.
If the supplied $map
contains an existing entry for the supplied $key
,
then the returned map contains an entry for that $key
whose value is obtained by applying
the supplied $action
to the existing value associated with that key.
Otherwise, the returned map contains an entry for the supplied $key
whose value is
obtained by applying the supplied $action
to an empty sequence.
The effect of the function call map:replace($MAP, $KEY, $VALUE)
is equivalent
to the result of the expression:
if (map:contains($MAP, $KEY)) then map:put($MAP, $KEY, $ACTION(map:get($MAP, $KEY))) else map:put($MAP, $KEY, $ACTION(())
The expression map:replace(map{1:"alpha", 2:"beta"}, 1, fn:upper-case#1)
returns map{1:"ALPHA", 2:"beta"}
.
The expression map:replace(map{1:"alpha", 2:"beta"}, 3, fn:upper-case#1)
returns map{1:"alpha", 2:"beta" 3:""}
.
The expression fold-left(("a", "b", "c", "a"), map{}, function($map, $key) {map:replace($map, $key, function($val){($val otherwise 0) + 1}})
returns map{"a":2, "b":1, "c":1}
.
Returns a map that typically contains one entry for each item in a supplied input sequence.
map:build ( |
||
$input |
as item()* , |
|
$key |
as function(item()) as xs:anyAtomicType? |
:= fn:identity#1 , |
$value |
as function(item()) as item()* |
:= fn:identity#1 , |
$combine |
as function(item()*, item()*) as item()* |
:= fn:op(',') |
) as map(*) |
This function is ·deterministic·, ·context-independent·, ·focus-independent·, and ·higher-order·.
Informally, the function processes each item in $input
in order. It calls the $key
function on
that item to obtain a key value, and the $value
function to obtain an associated value.
If the key is non-empty, then:
If the key is not already present in the target map, the processor adds a new key-value pair to the map, with that key and that value.
If the key is already present, the processor calls the $combine
function to combine the existing value
for the key with the new value, and replaces the entry with this combined value.
More formally, the result of the function is the result of the following expression:
fold-left($input, map{}, ->($map, $next) { let $nextKey := $key($next) let $nextValue := $value($next) return if (fn:exists($nextKey)) then if (map:contains($map, $nextKey)) then map:put($map, $nextKey, $combine($map($nextKey), $nextValue)) else map:put($map, $nextKey, $nextValue) else $map })
Although defined to process the input sequence in order, this is only relevant when combining the entries for duplicate keys.
The default function for both $key
and $value
is the identity function.
Although it is permitted to default both, this serves little purpose: usually at least one of these arguments
will be supplied.
The default action for combining entries with duplicate keys is to perform a sequence-concatenation of the corresponding values,
equivalent to the duplicates: combine
option on map:merge
. Other potentially useful
functions for combining duplicates include:
->($a, $b){$a}
Use the first value and discard the remainder
->($a, $b){$b}
Use the last value and discard the remainder
fn:concat(?, ",", ?)
Form the string-concatenation of the values, comma-separated
fn:op('+')
Compute the sum of the values
The expression map:build((), fn:string#1)
returns map{}
.
The expression map:build(1 to 10, ->{. mod 3})
returns map{0: (3, 6, 9), 1: (1, 4, 7, 10), 2: (2, 5, 8)}
. (Returns a map with one entry for each distinct value of . mod 3
. The
function to compute the value is the identity function, and duplicates are combined by
sequence concatenation.)
The expression map:build(1 to 5, value := fn:format-integer(?, "w")})
returns map{1: "one", 2: "two", 3: "three", 4: "four", 5: "five"}
. (Returns a map with five entries. The function to compute the key is an identity function, the
function to compute the value invokes fn:format-integer
.)
The expression map:build(("January", "February", "March", "April", "May",
"June", "July", "August", "September", "October", "November", "December"), fn:substring(?, 1, 1)})
returns map{"A": ("April", "August"), "D": ("December"), "F": ("February"), "J": ("January", "June", "July"),
"M": ("March", "May"), "N": ("November"), "O": ("October"), "S": ("September")}
.
The expression map:build(("apple", "apricot", "banana", "blueberry", "cherry"),
fn:substring(?, 1, 1), fn:string-length#1, fn:op("+"))
returns map{"a": 12, "b": 15, "c": 6}
. (Constructs a map where the key is the first character of an input item, and where the corresponding value
is the total string-length of the items starting with that character.)
The following expression creates a map whose keys are employee @ssn
values, and whose
corresponding values are the employee nodes:
map:build(//employee, ->{@ssn})
The following expression creates a map whose keys are employee @location
values, and whose
corresponding values represent the number of employees at each distinct location. Any employees that
lack an @location
attribute will be excluded from the result.
map:build(//employee, ->{@location}, ->{1}, fn:op("+"))
The following expression creates a map whose keys are employee @location
values, and whose
corresponding values contain the employee node for the highest-paid employee at each distinct location:
map:build(//employee, ->{@location}, combine := ->($a, $b){fn:highest(($a, $b), ->{xs:decimal(@salary)}))
The following expression creates a map allowing efficient access to every element in a document by means
of its fn:generate-id
value:
map:build(//*, fn:generate-id#1)
Accepted for version 4.0 2022-10-18
Because a map is a function item, functions that apply to functions also apply
to maps. A map is an anonymous function, so fn:function-name
returns the empty
sequence; fn:function-arity
always returns 1
.
Maps may be compared using the fn:deep-equal
function.
There is no function or operator to atomize a map or convert it to a string (other than fn:serialize
,
which can be used to serialize some maps as JSON texts).
An array is an additional kind of item. An array of size N is a mapping from the integers (1 to N) to a set of values, called the members of the array, each of which is an arbitrary sequence. Because an array is an item, and therefore a sequence, arrays can be nested.
The functions defined in this section use a conventional namespace prefix array
, which
is assumed to be bound to the namespace URI http://www.w3.org/2005/xpath-functions/array
.
As with all other values, arrays are treated as immutable.
For example, the array:reverse
function returns an array that differs from the supplied
array in the order of its members, but the supplied array is not changed by the operation. Two calls
on array:reverse
with the same argument will return arrays that are indistinguishable from
each other; there is no way of asking whether these are "the same array". Like sequences, arrays have no identity.
An array acts as a function from integer positions to associated values, so the
function call $array($index)
can be used to retrieve the array member at a given position.
The function corresponding to the array has the signature
function($index as xs:integer) as item()*
.
The fact that an array 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.
In the function definitions that follow, all the array functions are defined in terms of five primitives:
[]
represents the zero-length array (an array with no members).
$array($index)
returns the member at position $index
.
op:A2S($array)
converts the array to a sequence in which each member of the array
is replaced by a zero-arity function that returns the corresponding value. For example,
[(1,2), (3,4)]
becomes (function(){1,2}, function(){3,4})
.
op:S2A($seq)
is the inverse of op:A2S
: it takes as input a sequence
of zero-arity functions, and returns the array whose members are the results of evaluating
these functions. For example, (function(){1,2}, function(){3,4})
becomes [(1,2), (3,4)]
.
There are two operations on arrays for which the XPath language provides custom syntax:
array { $sequence }
constructs an array whose members are the items in $sequence
.
Every member of this array will be a singleton item.
This can be defined as fn:fold-left($sequence, [], function($x, $y){ op:array-concat($x, op:array-singleton($y))
[ E1, E2, E3, ..., En]
constructs an array in which E1
is the first member,
E2
is the second member, and so on. If N=0, the value is the empty array []
;
if N=1, the value is op:array-concat([], array { E1 })
, and if N > 1,
the value is op:array-concat(op:array-singleton(E1), [ E2, ... En ])
.
Function | Meaning |
---|---|
array:size |
Returns the number of members in the supplied array. |
array:empty |
Returns true if the supplied array contains no members. |
array:exists |
Returns true if the supplied array contains one or more members. |
array:get |
Returns the value at the specified position in the supplied array (counting from 1). |
array:put |
Returns an array containing all the members of a supplied array, except for one member which is replaced with a new value. |
array:replace |
Returns an array containing all the members of a supplied array, except for one member which is replaced with a new value, the new value being computed from the previous value. |
array:append |
Returns an array containing all the members of a supplied array, plus one additional member at the end. |
array:slice |
Returns an array containing selected members of a supplied input array based on their position. |
array:subarray |
Returns an array containing all members from a supplied array starting at a supplied position, up to a specified length. |
array:remove |
Returns an array containing all the members of the supplied array, except for the members at specified positions. |
array:insert-before |
Returns an array containing all the members of the supplied array, with one additional member at a specified position. |
array:head |
Returns the first member of an array, that is $array(1) . |
array:foot |
Returns the last member of an array, that is $array(array:size($array)) . |
array:tail |
Returns an array containing all members except the first from a supplied array. |
array:trunk |
Returns an array containing all members except the last from a supplied array. |
array:reverse |
Returns an array containing all the members of a supplied array, but in reverse order. |
array:join |
Concatenates the contents of several arrays into a single array. |
array:for-each |
Returns an array whose size is the same as array:size($array) , in which
each member is computed by applying $function to the corresponding member of
$array . |
array:filter |
Returns an array containing those members of the $array for which
$predicate returns true. |
array:fold-left |
Evaluates the supplied function cumulatively on successive members of the supplied array. |
array:fold-right |
Evaluates the supplied function cumulatively on successive values of the supplied array. |
array:for-each-pair |
Returns an array obtained by evaluating the supplied function once for each pair of members at the same position in the two supplied arrays. |
array:sort |
Returns an array containing all the members of the supplied array, sorted according to the value of a sort key supplied as a function. |
array:flatten |
Replaces any array appearing in a supplied sequence with the members of the array, recursively. |
array:from-sequence |
Returns an array obtained by evaluating the supplied function once for each item in the input sequence. |
array:partition |
Partitions a sequence of items into a sequence of arrays containing the same items, starting a new partition when a supplied condition is true. |
array:index-where |
Returns the position in an input array of members that match a supplied predicate. |
Returns the number of members in the supplied array.
array:size ( |
||
$array |
as array(*) |
|
) as xs:integer |
This function is ·deterministic·, ·context-independent·, and ·focus-independent·.
Informally, the function returns the number of members in the array.
More formally, the function returns the value of count(op:A2S($array))
.
Note that because an array is an item, the fn:count
function when applied to an array always returns 1 (one).
The expression array:size(["a", "b", "c"])
returns 3
.
The expression array:size(["a", ["b", "c"]])
returns 2
.
The expression array:size([ ])
returns 0
.
The expression array:size([[ ]])
returns 1
.
Returns true if the supplied array contains no members.
array:empty ( |
||
$array |
as array(*) |
|
) as xs:boolean |
This function is ·deterministic·, ·context-independent·, and ·focus-independent·.
The function returns true if and only if $array
contains no members, that is,
if array:size($array) eq 0
.
The test for emptiness is not the same as the test used by the
xsl:on-empty
instruction in XSLT. For example, an array
is not considered empty by this function if it contains a single
member that is itself an empty array.
The expression array:empty(["a", "b", "c"])
returns false()
.
The expression array:empty([])
returns true()
.
The expression array:empty([[]])
returns false()
.
The expression array:empty([()])
returns false()
.
Proposed for 4.0, see issue 229
Returns true if the supplied array contains one or more members.
array:exists ( |
||
$array |
as array(*) |
|
) as xs:boolean |
This function is ·deterministic·, ·context-independent·, and ·focus-independent·.
The function returns true if and only if $array
contains one or more members, that is,
if array:size($array) gt 0
.
The function name is chosen by analogy with fn:exists
. Note that the function tests whether
any array members exist, not whether the array itself exists. A function such as:
function($a as array(*)?) as xs:boolean { return array:exists($a) }
will raise a type error (rather than returning false) if the argument $a
is
an empty sequence, because array:exists
does not allow the argument to be
an empty sequence.
The expression array:exists(["a", "b", "c"])
returns true()
.
The expression array:exists([])
returns false()
.
The expression array:exists([[]])
returns true()
.
The expression array:exists([()])
returns true()
.
Proposed for 4.0, see issue 229
Returns the value at the specified position in the supplied array (counting from 1).
array:get ( |
||
$array |
as array(*) , |
|
$position |
as xs:integer |
|
) as item()* |
This function is ·deterministic·, ·context-independent·, and ·focus-independent·.
Informally, the function returns the member at a specified position in the array.
More formally, the function returns the value of op:A2S($array)[$position]()
.
A dynamic error occurs [err:FOAY0001] if $position
is not in the range 1 to
array:size($array)
inclusive.
The expression ["a", "b", "c"] => array:get(2)
returns "b"
.
The expression ["a", ["b", "c"]] => array:get(2)
returns ["b", "c"]
.
Returns an array containing all the members of a supplied array, except for one member which is replaced with a new value.
array:put ( |
||
$array |
as array(*) , |
|
$position |
as xs:integer , |
|
$member |
as item()* |
|
) as array(*) |
This function is ·deterministic·, ·context-independent·, and ·focus-independent·.
Informally, the result is an array whose size is array:size($array)
, in which all
members in positions other than $position
are the same as the members in the corresponding position
of $array
, and the member in position $position
is $member
.
More formally, the result is the value of the expression
$array => array:remove($position) => array:insert-before($position, $member)
.
A dynamic error occurs [err:FOAY0001] if $position
is not in the range 1 to
array:size($array)
inclusive.
This error will always occur if $array
is empty.
The expression array:put(["a", "b", "c"], 2, "d")
returns ["a", "d", "c"]
.
The expression array:put(["a", "b", "c"], 2, ("d", "e"))
returns ["a", ("d", "e"), "c"]
.
The expression array:put(["a"], 1, ["d", "e"])
returns [["d", "e"]]
.
Returns an array containing all the members of a supplied array, except for one member which is replaced with a new value, the new value being computed from the previous value.
array:replace ( |
||
$array |
as array(*) , |
|
$position |
as xs:integer , |
|
$action |
as function(item()*) as item()* |
|
) as array(*) |
This function is ·deterministic·, ·context-independent·, and ·focus-independent·.
Informally, the result is an array whose size is array:size($array)
, in which all
members in positions other than $position
are the same as the members in the corresponding position
of $array
, and the member in position $position
is the result of applying
the $action
function to the original value in that position.
More formally, the result is the value of the expression
$array => array:remove($position) => array:insert-before($position, $action($array($position)))
.
A dynamic error occurs [err:FOAY0001]
if $position
is not in the range 1 to
array:size($array)
inclusive.
This error will always occur if $array
is empty.
The expression array:replace([10, 11, 12], 2, ->{.+10})
returns [10, 21, 12]
.
The expression array:replace(["a", "b", "c"], 2, concat(?, "x"))
returns ["a", "bx", "c"]
.
The expression array:replace([("a", "b"), ("c", "d")], 2, fn:reverse#1)
returns [("a", "b"), ("d", "c")]
.
Returns an array containing all the members of a supplied array, plus one additional member at the end.
array:append ( |
||
$array |
as array(*) , |
|
$add |
as item()* |
|
) as array(*) |
This function is ·deterministic·, ·context-independent·, and ·focus-independent·.
Informally, the result is an array whose size is array:size($array) + 1
, in which all
members in positions 1 to array:size($array)
are the same as the members in the corresponding position
of $array
, and the member in position array:size($array) + 1
is $add
.
More formally, the result is the value of the expression
(op:A2S($array), function(){$add}) => op:S2A()
.
The expression array:append(["a", "b", "c"], "d")
returns ["a", "b", "c", "d"]
.
The expression array:append(["a", "b", "c"], ("d", "e"))
returns ["a", "b", "c", ("d", "e")]
.
The expression array:append(["a", "b", "c"], ["d", "e"])
returns ["a", "b", "c", ["d", "e"]]
.
Returns an array containing selected members of a supplied input array based on their position.
array:slice ( |
||
$input |
as array(*) , |
|
$start |
as xs:integer? |
:= () , |
$end |
as xs:integer? |
:= () , |
$step |
as xs:integer? |
:= () |
) as array(*) |
This function is ·deterministic·, ·context-independent·, and ·focus-independent·.
Returns the value of op:A2S($input) => fn:slice($start, $end, $step) => op:S2A()
The function is formally defined by converting the array to a sequence, applying
fn:slice
to this sequence, and then converting the resulting sequence
back to an array.
Note that unlike other operations on arrays, there are no out-of-bounds errors for inappropriate
values of $start
, $end
, or $step
.
let $in := ['a', 'b', 'c', 'd', 'e']
The expression array:slice($in, start:2, end:4)
returns ["b", "c", "d"]
.
The expression array:slice($in, start:2)
returns ["b", "c", "d", "e"]
.
The expression array:slice($in, end:2)
returns ["a", "b"]
.
The expression array:slice($in, start:3, end:3)
returns ["c"]
.
The expression array:slice($in, start:4, end:3)
returns ["d", "c"]
.
The expression array:slice($in, start:2, end:5, step:2)
returns ["b", "d"]
.
The expression array:slice($in, start:5, end:2, step:-2)
returns ["e", "c"]
.
The expression array:slice($in, start:2, end:5, step:-2)
returns []
.
The expression array:slice($in, start:5, end:2, step:2)
returns []
.
The expression array:slice($in)
returns ["a", "b", "c", "d", "e"]
.
The expression array:slice($in, start:-1)
returns ["e"]
.
The expression array:slice($in, start:-3)
returns ["c", "d", "e"]
.
The expression array:slice($in, end:-2)
returns ]"a", "b", "c", "d"]
.
The expression array:slice($in, start:2, end:-2)
returns ["b", "c", "d"]
.
The expression array:slice($in, start:-2, end:2)
returns ["d", "c", "b"]
.
The expression array:slice($in, start:-4, end:-2)
returns ["b", "c", "d"]
.
The expression array:slice($in, start:-2, end:-4)
returns ["d", "c", "b"]
.
The expression array:slice($in, start:-4, end:-2, step:2)
returns ["b", "d"]
.
The expression array:slice($in, start:-2, end:-4, step:-2)
returns ["d", "b"]
.
The expression array:slice(["a", "b", "c", "d"], 0)
returns []
.
Returns an array containing all members from a supplied array starting at a supplied position, up to a specified length.
array:subarray ( |
||
$array |
as array(*) , |
|
$start |
as xs:integer |
|
) as array(*) |
array:subarray ( |
||
$array |
as array(*) , |
|
$start |
as xs:integer , |
|
$length |
as xs:integer |
|
) as array(*) |
This function is ·deterministic·, ·context-independent·, and ·focus-independent·.
Except in error cases,
the two-argument version of the function returns the same result as the three-argument
version when called with $length
equal to the value of array:size($array) -
$start + 1
.
Except in error cases, the result of the three-argument version of the function is the
value of the expression
op:A2S($array) => fn:subsequence($start, $length) => op:S2A()
.
A dynamic error is raised [err:FOAY0001] if $start
is less than one
or greater than array:size($array) + 1
.
For the three-argument version of the function:
A dynamic error is raised [err:FOAY0002]
if $length
is less than zero.
A dynamic error is raised [err:FOAY0001]
if $start + $length
is greater than array:size($array) + 1
.
The value of $start
can be equal to array:size($array) + 1
provided that $length
is either equal to zero or omitted. In this case the result will be an empty array.
The expression array:subarray(["a", "b", "c", "d"], 2)
returns ["b", "c", "d"]
.
The expression array:subarray(["a", "b", "c", "d"], 5)
returns [ ]
.
The expression array:subarray(["a", "b", "c", "d"], 2, 0)
returns [ ]
.
The expression array:subarray(["a", "b", "c", "d"], 2, 1)
returns ["b"]
.
The expression array:subarray(["a", "b", "c", "d"], 2, 2)
returns ["b", "c"]
.
The expression array:subarray(["a", "b", "c", "d"], 5, 0)
returns [ ]
.
The expression array:subarray([ ], 1, 0)
returns [ ]
.
Returns an array containing all the members of the supplied array, except for the members at specified positions.
array:remove ( |
||
$array |
as array(*) , |
|
$positions |
as xs:integer* |
|
) as array(*) |
This function is ·deterministic·, ·context-independent·, and ·focus-independent·.
Informally, the function returns an array of size array:size($array) - fn:count(fn:distinct-values($positions))
containing all members from $array
except the members whose position (counting from 1) is present in the sequence $positions
.
The order of the remaining members is preserved.
More formally, the result of the function, except in error cases, is given by the expression
op:A2S($array)[not(position() = $positions)] => op:S2A()
.
A dynamic error is raised [err:FOAY0001] if any integer in $positions
is not in the range 1 to
array:size($array)
inclusive. By implication, an error occurs if $array
is empty, unless $positions
is also empty.
The expression array:remove(["a", "b", "c", "d"], 1)
returns ["b", "c", "d"]
.
The expression array:remove(["a", "b", "c", "d"], 2)
returns ["a", "c", "d" ]
.
The expression array:remove(["a"], 1)
returns [ ]
.
The expression array:remove(["a", "b", "c", "d"], 1 to 3)
returns ["d"]
.
The expression array:remove(["a", "b", "c", "d"], ())
returns ["a", "b", "c", "d"]
.
Returns an array containing all the members of the supplied array, with one additional member at a specified position.
array:insert-before ( |
||
$array |
as array(*) , |
|
$position |
as xs:integer , |
|
$member |
as item()* |
|
) as array(*) |
This function is ·deterministic·, ·context-independent·, and ·focus-independent·.
Informally, the function returns an array of size array:size($array) + 1
containing all members from $array
whose position is less than $position
, then a new member given by $member
, and
then all members from $array
whose position is greater than or equal to $position
.
Positions are counted from 1.
More formally, except in error cases, the result is the value of the expression
op:A2S($array) => fn:insert-before($position, function(){$member}) => op:S2A()
.
A dynamic error occurs [err:FOAY0001] if $position
is not in the range 1 to
array:size($array) + 1
inclusive.
Setting $position
to 1 has the effect of prepending the new member at the start of the array. Setting $position
to the value array:size($array) + 1
delivers the same result as array:append($array, $member)
.
The expression array:insert-before(["a", "b", "c", "d"], 3, ("x", "y"))
returns ["a", "b", ("x", "y"), "c", "d"]
.
The expression array:insert-before(["a", "b", "c", "d"], 5, ("x", "y"))
returns ["a", "b", "c", "d", ("x", "y")]
.
The expression array:insert-before(["a", "b", "c", "d"], 3, ["x", "y"])
returns ["a", "b", ["x", "y"], "c", "d"]
.
Returns the first member of an array, that is $array(1)
.
array:head ( |
||
$array |
as array(*) |
|
) as item()* |
This function is ·deterministic·, ·context-independent·, and ·focus-independent·.
The function returns first member of $array
, that is the value of array:get($array, 1)
.
A dynamic error occurs [err:FOAY0001] if $array
is empty.
The expression array:head([5, 6, 7, 8])
returns 5
.
The expression array:head([["a", "b"], ["c", "d"]])
returns ["a", "b"]
.
The expression array:head([("a", "b"), ("c", "d")])
returns "a", "b"
.
Returns the last member of an array, that is $array(array:size($array))
.
array:foot ( |
||
$array |
as array(*) |
|
) as item()* |
This function is ·deterministic·, ·context-independent·, and ·focus-independent·.
The function returns last member of $array
, that is the value of array:get($array, array:size($array))
.
A dynamic error occurs [err:FOAY0001] if $array
is empty.
The expression array:foot([5, 6, 7, 8])
returns 8
.
The expression array:foot([["a", "b"], ["c", "d"]])
returns ["c", "d"]
.
The expression array:foot([("a", "b"), ("c", "d")])
returns "c", "d"
.
Proposed for 4.0, see issue 97
Returns an array containing all members except the first from a supplied array.
array:tail ( |
||
$array |
as array(*) |
|
) as array(*) |
This function is ·deterministic·, ·context-independent·, and ·focus-independent·.
The function returns an array containing all members of the supplied array except the first,
that is array:remove($array, 1)
.
A dynamic error occurs [err:FOAY0001] if $array
is empty.
If the supplied array contains exactly one member, the result will be an empty array.
The expression array:tail([5, 6, 7, 8])
returns [6, 7, 8]
.
The expression array:tail([5])
returns [ ]
.
Returns an array containing all members except the last from a supplied array.
array:trunk ( |
||
$array |
as array(*) |
|
) as array(*) |
This function is ·deterministic·, ·context-independent·, and ·focus-independent·.
The function returns an array containing all members of the supplied array except the last,
that is array:remove($array, array:size($array))
.
A dynamic error occurs [err:FOAY0001] if $array
is empty.
If the supplied array contains exactly one member, the result will be an empty array.
The expression array:trunk([5, 6, 7, 8])
returns [5, 6, 7]
.
The expression array:trunk([5])
returns [ ]
.
Proposed for 4.0, see issue 97
Returns an array containing all the members of a supplied array, but in reverse order.
array:reverse ( |
||
$array |
as array(*) |
|
) as array(*) |
This function is ·deterministic·, ·context-independent·, and ·focus-independent·.
The function returns the result of the expression:
op:A2S($array) => fn:reverse() => op:S2A()
The expression array:reverse(["a", "b", "c", "d"])
returns ["d", "c", "b", "a"]
.
The expression array:reverse([("a", "b"), ("c", "d")])
returns [("c", "d"), ("a", "b")]
.
The expression array:reverse([(1 to 5)])
returns [(1, 2, 3, 4, 5)]
.
The expression array:reverse([])
returns []
.
Concatenates the contents of several arrays into a single array.
array:join ( |
||
$arrays |
as array(*)* |
|
) as array(*) |
This function is ·deterministic·, ·context-independent·, and ·focus-independent·.
Informally, the function concatenates the members of several arrays into a single array.
More formally, the function returns the result of
($arrays ! op:A2S(.)) => op:S2A()
.
The expression array:join(())
returns [ ]
.
The expression array:join([1, 2, 3])
returns [1, 2, 3]
.
The expression array:join((["a", "b"], ["c", "d"]))
returns ["a", "b", "c", "d"]
.
The expression array:join((["a", "b"], ["c", "d"], [ ]))
returns ["a", "b", "c", "d"]
.
The expression array:join((["a", "b"], ["c", "d"], [["e", "f"]]))
returns ["a", "b", "c", "d", ["e", "f"]]
.
Returns an array whose size is the same as array:size($array)
, in which
each member is computed by applying $function
to the corresponding member of
$array
.
array:for-each ( |
||
$array |
as array(*) , |
|
$action |
as function(item()*) as item()* |
|
) as array(*) |
This function is ·deterministic·, ·context-independent·, ·focus-independent·, and ·higher-order·.
Informally, the function returns an array whose members are obtained by applying
the supplied $function
to each member of the input array in turn.
More formally, the function returns the result of the expression
(for $member in op:A2S($array) return function(){$action($member())) => op:S2A()
.
The expression array:for-each(["A", "B", 1, 2], function($z) {$z instance of xs:integer})
returns [false(), false(), true(), true()]
.
The expression array:for-each(["the cat", "sat", "on the mat"], fn:tokenize#1)
returns [("the", "cat"), "sat", ("on", "the", "mat")]
.
The expression array:for-each([["the", "cat"], ["sat"], ["on", "the", "mat"]], array:flatten#1)
returns [("the", "cat"), "sat", ("on", "the", "mat")]
.
The expression array:for-each([["the", "cat"], ["sat"], ["on", "the", "mat"]], ->($m){->{$m}})
returns [("the", "cat"), "sat", ("on", "the", "mat")]
.
The expression array:for-each($A, ->($m){->{$m}})
returns a sequence of zero-arity functions,
one for each item in the array, where the zero-arity function encapsulates the value of one array member. This
construct can be useful as a bridge from arrays to sequences. For example, a function to reverse the order of
members in an array can be implemented as $A => for-each(->$m{->{$m}}) => reverse()
Returns an array containing those members of the $array
for which
$predicate
returns true.
array:filter ( |
||
$array |
as array(*) , |
|
$predicate |
as function(item()*) as xs:boolean |
|
) as array(*) |
This function is ·deterministic·, ·context-independent·, ·focus-independent·, and ·higher-order·.
Informally, the function returns an array containing those members of the input array that satisfy the supplied predicate.
More formally, the function returns the result of the expression
op:A2S($array) => fn:filter(function($m){$predicate($m())}) => op:S2A()
.
As a consequence of the function signature and the function calling rules, a type error occurs if the supplied
function $function
returns anything other than a single xs:boolean
item; there is no conversion
to an effective boolean value.
The expression array:filter(["A", "B", 1, 2], function($x) {$x instance of xs:integer})
returns [1, 2]
.
The expression array:filter(["the cat", "sat", "on the mat"], function($s){fn:count(fn:tokenize($s)) gt 1})
returns ["the cat", "on the mat"]
.
The expression array:filter(["A", "B", "", 0, 1], boolean#1)
returns ["A", "B", 1]
.
Evaluates the supplied function cumulatively on successive members of the supplied array.
array:fold-left ( |
||
$array |
as array(*) , |
|
$zero |
as item()* , |
|
$action |
as function(item()*, item()*) as item()* |
|
) as item()* |
This function is ·deterministic·, ·context-independent·, ·focus-independent·, and ·higher-order·.
The result of the function is the value of the expression
op:A2S($array) => fn:fold-left($zero, function($a, $b){$action($a, $b()})
If the supplied array is empty, the function returns $zero
.
If the supplied array contains a single member $m
, the function returns $zero => $action($m)
.
If the supplied array contains two members $m
and $n
, the function returns
$zero => $action($m) => $action($n)
; and similarly for an input array with more than two members.
The expression array:fold-left([true(), true(), false()], true(), function($x, $y){$x and $y})
returns false()
. (Returns true if every member of the input array has an effective boolean value of true()
.)
The expression array:fold-left([true(), true(), false()], false(), function($x, $y){$x or $y})
returns true()
. (Returns true if at least one member of the input array has an effective boolean value of true()
.)
The expression array:fold-left([1,2,3], [], function($x, $y){[$x, $y]})
returns [[[[], 1], 2], 3]
.
Evaluates the supplied function cumulatively on successive values of the supplied array.
array:fold-right ( |
||
$array |
as array(*) , |
|
$zero |
as item()* , |
|
$action |
as function(item()*, item()*) as item()* |
|
) as item()* |
This function is ·deterministic·, ·context-independent·, ·focus-independent·, and ·higher-order·.
The result of the function is the value of the expression
op:A2S($array) => fn:fold-right($zero, function($a, $b){$action($a, $b()})
If the supplied array is empty, the function returns $zero
.
If the supplied array contains a single member $m
, the function returns $action($m, $zero)
.
If the supplied array contains two members $m
and $n
, the function returns
$action($m, $action($n, $zero))
; and similarly for an input array with more than two members.
The expression array:fold-right([true(), true(), false()], true(), function($x, $y){$x and $y})
returns false()
. (Returns true if every member of the input array has an effective boolean value of true()
.)
The expression array:fold-right([true(), true(), false()], false(), function($x, $y){$x or $y})
returns true()
. (Returns true if at least one member of the input array has an effective boolean value of true()
.)
The expression array:fold-right([1,2,3], [], function($x, $y){[$x, $y]})
returns [1, [2, [3, []]]]
.
Returns an array obtained by evaluating the supplied function once for each pair of members at the same position in the two supplied arrays.
array:for-each-pair ( |
||
$array1 |
as array(*) , |
|
$array2 |
as array(*) , |
|
$action |
as function(item()*, item()*) as item()* |
|
) as array(*) |
This function is ·deterministic·, ·context-independent·, ·focus-independent·, and ·higher-order·.
The function returns the result of the expression
fn:for-each-pair(op:A2S($array1), op:A2S($array2), function($m, $n) {function(){$action($m(), $n()}}) => op:S2A()
.
If the arrays have different size, excess members in the longer array are ignored.
The expression array:for-each-pair(["A", "B", "C"], [1, 2, 3], function($x, $y) { array {$x, $y}})
returns [["A", 1], ["B", 2], ["C", 3]]
.
The expression let $A := ["A", "B", "C", "D"] return array:for-each-pair($A, array:tail($A), concat#2)
returns ["AB", "BC", "CD"]
.
Returns an array containing all the members of the supplied array, sorted according to the value of a sort key supplied as a function.
array:sort ( |
||
$array |
as array(*) |
|
) as array(*) |
array:sort ( |
||
$array |
as array(*) , |
|
$collation |
as xs:string? |
|
) as array(*) |
array:sort ( |
||
$array |
as array(*) , |
|
$collation |
as xs:string? , |
|
$key |
as function(item()*) as xs:anyAtomicType* |
|
) as array(*) |
The one-argument form of this function is ·deterministic·, ·context-dependent·, and ·focus-independent·. It depends on collations.
The two-argument form of this function is ·deterministic·, ·context-dependent·, and ·focus-independent·. It depends on collations.
The three-argument form of this function is ·deterministic·, ·context-independent·, ·focus-independent·, and ·higher-order·.
Calling the single-argument version of the function is equivalent to calling the two-argument form
with default-collation()
as the second argument: that is, it sorts the members of an array according
to the typed value of the items, using the default collation to compare strings.
Calling the two-argument version of the function is equivalent to calling the three-argument form
with fn:data#1
as the third argument: that is, it sorts the members of an array according
to the typed value of the items, using a specified collation to compare strings.
In the case of both array:sort#2
and array:sort#3
, supplying an empty
sequence as the second argument is equivalent to supplying fn:default-collation()
. For more
information on collations see 5.3.5 Choosing a collation.
The result of array:sort#3
is the value of the expression
op:A2S($array) => fn:sort($collation, function($x){$key($x())}) => op:S2A()
If the set of computed sort keys contains values that are not comparable using the le
operator then the sort
operation will fail with a dynamic error.
The expression array:sort([1, 4, 6, 5, 3])
returns [1, 3, 4, 5, 6]
.
The expression array:sort([1, -2, 5, 10, -10, 10, 8], (), fn:abs#1)
returns [1, -2, 5, 8, 10, -10, 10]
.
The expression array:sort([(1,0), (1,1), (0,1), (0,0)])
returns [(0,0), (0,1), (1,0), (1,1)]
.
To sort an array of strings $in
using Swedish collation:
let $SWEDISH := "http://www.w3.org/2013/collation/UCA?lang=se" return array:sort($in, $SWEDISH)
To sort an array of maps representing employees, using last name as the major sort key and first name as the minor sort key, with the default collation:
array:sort($employees, (), function($emp) {$emp?name?last, $emp?name?first})
Replaces any array appearing in a supplied sequence with the members of the array, recursively.
array:flatten ( |
||
$input |
as item()* |
|
) as item()* |
This function is ·deterministic·, ·context-independent·, and ·focus-independent·.
The function processes the items in the supplied sequence $input
as follows:
An item that is an array is replaced by its members, retaining order.
Any other item is retained unchanged.
The process is then repeated so long as the sequence contains an array among its items.
The function is equivalent to the following XQuery implementation (assuming static typing is not in force):
declare function flatten ($S as item()*) {
for $s in $S return (
typeswitch($s)
case $a as array(*) return flatten($a?*)
default return $s
)}
The argument to the function will often be a single array item, but this is not essential.
Unlike atomization, this function retains any nodes contained in the array.
The expression array:flatten([1, 4, 6, 5, 3])
returns (1, 4, 6, 5, 3)
.
The expression array:flatten(([1, 2, 5], [[10, 11], 12], [], 13))
returns (1, 2, 5, 10, 11, 12, 13)
.
The expression array:flatten([(1,0), (1,1), (0,1), (0,0)])
returns (1, 0, 1, 1, 0, 1, 0, 0)
.
Returns an array obtained by evaluating the supplied function once for each item in the input sequence.
array:from-sequence ( |
||
$input |
as item()* |
|
) as array(*) |
array:from-sequence ( |
||
$input |
as item()* , |
|
$action |
as function(item()) as item()* |
|
) as array(*) |
This function is ·deterministic·, ·context-independent·, ·focus-independent·, and ·higher-order·.
If the function is called with one argument, the effect is the same as calling the two-argument
function with fn:identity#1
as the second argument.
Informally, array:from-sequence#2
applies the supplied function to each item
in the input sequence, and the resulting sequence becomes one member of the returned array.
More formally, array:from-sequence#2
returns the result of the expression:
fn:for-each($input, function($x)(function(){$action($x)})) => op:S2A()
The single-argument function array:from-sequence($input)
is equivalent to the XPath
expression array{$input}
, but it is useful to have this available as a function.
The two-argument form facilitates the construction of arrays whose members are arbitrary sequences.
The expression array:from-sequence(1 to 5)
returns [1, 2, 3, 4, 5]
.
The expression array:from-sequence(1 to 5, ->{2*.})
returns [2, 4, 6, 8, 10]
.
The expression array:from-sequence(1 to 5, ->{1 to .})
returns [1, (1,2), (1,2,3), (1,2,3,4), (1,2,3,4,5)]
.
The expression array:from-sequence(("red", "green", "blue"), fn:characters#1)
returns [("r", "e", "d"), ("g", "r", "e", "e", "n"), ("b", "l", "u", "e)]
.
The expression array:from-sequence(1 to 5, ->{array{1 to .}})
returns [[1], [1,2], [1,2,3], [1,2,3,4], [1,2,3,4,5]]
.
The expression array:from-sequence(1 to 20, ->{array{1 to .}})
returns [[1], [1,2], [1,2,3], [1,2,3,4], [1,2,3,4,5]]
.
Partitions a sequence of items into a sequence of arrays containing the same items, starting a new partition when a supplied condition is true.
array:partition ( |
||
$input |
as item()* , |
|
$break-when |
as function(item()*, item()) as xs:boolean |
|
) as array(*)+ |
This function is ·deterministic·, ·context-independent·, and ·focus-independent·.
Informally, the function processes the items in the input sequence in order, and for each item
it calls the supplied $break-when
function with two arguments: the contents of the
current partition (initially an empty sequence), and the current item in the input sequence. If
the $break-when
function returns true, the current partition is added to the result
and a new current partition is created, initially containing the current item. If the $break-when
function returns false, the current item is added to the current partition.
More formally, the function returns the result of the expression:
fn:fold-left($input, (), function($a, $b) { if (empty($a) or $break-when(array:slice($a, start:-1)?*, $b)) then ($a, [$b]) else array:replace($a, array:size($a), function($m){$m, $b}) }
The function enables a variety of positional grouping problems to be solved. For example:
array:partition($input, function($a, $b){count($a) eq 3}
partitions a sequence into fixed size groups of length 3.
array:partition($input, function($a, $b){boolean($b/self::h1)}
starts a new group whenever an h1
element is encountered.
array:partition($input, function($a, $b){$b lt $a[last()]}
starts a new group whenever an item is encountered whose value is less than
the value of the previous item.
The expression array:partition(("Anita", "Anne", "Barbara", "Catherine", "Christine"),
->($x, $y){fn:substring($x[last()],1,1) ne fn:substring($y,1,1)})
returns (["Anita", "Anne"], ["Barbara"], ["Catherine", "Christine"])
.
The expression array:partition((1, 2, 3, 4, 5, 6), function($a, $b){count($a) eq 2})
returns ([1, 2], [3, 4], [5, 6])
.
The expression array:partition((1, 4, 6, 3, 1, 1), function($a, $b){sum($a) ge 5})
returns ([1, 4], [6], [3, 1, 1])
.
The expression array:partition(tokenize("In the beginning was the word"), function($a, $b){sum(($a,$b)!string-length() gt 10)}
returns (["In", "the"], ["beginning"], ["was", "the", "word"])
.
The expression array:partition((1, 2, 3, 6, 7, 9, 10), function($a, $b){$a ne $b[last()]+1})
returns ([1, 2, 3], [6, 7], [9, 10])
.
Returns the position in an input array of members that match a supplied predicate.
array:index-where ( |
||
$array |
as array(*) , |
|
$predicate |
as function(item()*) as xs:boolean |
|
) as xs:integer* |
This function is ·deterministic·, ·context-independent·, and ·focus-independent·.
The result of the function is a sequence of integers, in monotonic ascending order, representing the 1-based positions in the input array of those members for which the supplied predicate function returns true.
More formally, the function returns the result of the expression:
fn:index-of(array:for-each($input, $predicate)?*, true())
The expression array:index-where([], fn:boolean#1)
returns ()
.
The expression array:index-where([0, (), 4, 9], fn:boolean#1)
returns (3, 4)
.
The expression array:index-where(array{1 to 10}, ->{. mod 2 = 0}))
returns (2, 4, 6, 8, 10)
.
The expression array:index-where(["January", "February", "March", "April", "May",
"June", "July", "August", "September", "October", "November", "December"], fn:contains(?, "r"))
returns (1, 2, 3, 4, 9, 10, 11, 12)
.
The expression array:index-where([(1, 2, 3), (4, 5, 6), (7, 8)], ->($m){count($m) = 3})
returns (1, 2)
.
Approved 2022-12-13 for inclusion in 4.0 - issue #114
JSON is a popular format for exchange of structured data on the web: it is specified in [RFC 7159]. This section describes facilities allowing JSON data to be converted to and from XDM values.
This specification describes two ways of representing JSON data losslessly using XDM constructs. The first method uses XDM maps to represent JSON objects, and XDM arrays to represent JSON arrays. The second method represents all JSON constructs using XDM element and attribute nodes.
This section defines a mapping from JSON data to XDM maps and arrays. Two functions are available
to support this mapping: fn:parse-json
and fn:serialize
(with options
selecting JSON as the output method).
The fn:parse-json
function will accept any JSON text as input, and converts it
to XDM data values. The fn:serialize
function (with JSON as the output method) will accept any XDM
value produced using fn:parse-json
and convert it back to the original JSON text
(subject to insignificant variations such as reordering the properties in a JSON object).
Note:
The conversion is lossless if recommended JSON good practice is followed. Information may however be lost if (a) JSON numbers are not exactly representable as double-precision floating point, or (b) duplicate key values appear within a JSON object.
The representation of JSON data produced by the fn:parse-json
function
has been chosen with ease of manipulation as a design aim. For example, a simple JSON object
such as {"Sun":1, "Mon":2, "Tue":3, ...}
produces a simple map, so if the result
of parsing is held in $weekdays
, the number for a given weekday can be extracted
using an expression such as $weekdays?Tue
. Similarly, a simple array such as
["Sun", "Mon", "Tue", ...]
produces an array that can be addressed as, for example,
$weekdays(3)
. A more deeply nested structure can be addressed in a similar way:
for example if the JSON text is an array of person objects, each of which has a property named
"phones" which is an array of strings containing phone numbers, then the first phone number of
each person in the data can be addressed as $data?phones(1)
.
This section defines a mapping from JSON data to XML (specifically, to XDM element and attribute nodes). A
function fn:json-to-xml
is provided to take a JSON string as input and convert it
to the XML representation, and a second function fn:xml-to-json
performs the reverse operation.
The XML representation is designed to be capable of representing any valid JSON text including one that uses characters which are not valid in XML. The transformation is normally lossless: that is, distinct JSON texts convert to distinct XML representations. When converting JSON to XML, options are provided to reject unsupported characters, to replace them with a substitute character, or to leave them in backslash-escaped form.
Note:
The conversion is lossless if recommended JSON good practice is followed. Information may however be lost if (a) JSON numbers are not exactly representable as double-precision floating point, or (b) duplicate key values appear within a JSON object.
The following example demonstrates the correspondence of a JSON text and the corresponding XML representation.
Consider the following JSON text:
{ "desc" : "Distances between several cities, in kilometers.", "updated" : "2014-02-04T18:50:45", "uptodate": true, "author" : null, "cities" : { "Brussels": [ {"to": "London", "distance": 322}, {"to": "Paris", "distance": 265}, {"to": "Amsterdam", "distance": 173} ], "London": [ {"to": "Brussels", "distance": 322}, {"to": "Paris", "distance": 344}, {"to": "Amsterdam", "distance": 358} ], "Paris": [ {"to": "Brussels", "distance": 265}, {"to": "London", "distance": 344}, {"to": "Amsterdam", "distance": 431} ], "Amsterdam": [ {"to": "Brussels", "distance": 173}, {"to": "London", "distance": 358}, {"to": "Paris", "distance": 431} ] } }
The XML representation of this text is as follows. Whitespace is included in the XML representation for purposes of illustration,
but it will not necessarily be present in the output of the
json-to-xml
function.
<map xmlns="http://www.w3.org/2005/xpath-functions"> <string key='desc'>Distances between several cities, in kilometers.</string> <string key='updated'>2014-02-04T18:50:45</string> <boolean key="uptodate">true</boolean> <null key="author"/> <map key='cities'> <array key="Brussels"> <map> <string key="to">London</string> <number key="distance">322</number> </map> <map> <string key="to">Paris</string> <number key="distance">265</number> </map> <map> <string key="to">Amsterdam</string> <number key="distance">173</number> </map> </array> <array key="London"> <map> <string key="to">Brussels</string> <number key="distance">322</number> </map> <map> <string key="to">Paris</string> <number key="distance">344</number> </map> <map> <string key="to">Amsterdam</string> <number key="distance">358</number> </map> </array> <array key="Paris"> <map> <string key="to">Brussels</string> <number key="distance">265</number> </map> <map> <string key="to">London</string> <number key="distance">344</number> </map> <map> <string key="to">Amsterdam</string> <number key="distance">431</number> </map> </array> <array key="Amsterdam"> <map> <string key="to">Brussels</string> <number key="distance">173</number> </map> <map> <string key="to">London</string> <number key="distance">358</number> </map> <map> <string key="to">Paris</string> <number key="distance">431</number> </map> </array> </map> </map>
An XSD 1.0 schema for the XML representation is provided in C.2 Schema for the result of fn:json-to-xml.
It is not necessary to import this schema into the static context unless the stylesheet or query
makes explicit reference to the components defined in the schema. If the stylesheet or query does import a schema
for the namespace http://www.w3.org/2005/xpath-functions
, then:
Unless the host language specifies otherwise, the processor (if it is schema-aware) must recognize an import declaration for this namespace, whether or not a schema location is supplied.
If a schema location is provided, then the schema document at that location must be equivalent to the schema document at C.2 Schema for the result of fn:json-to-xml; the effect if it is not equivalent is ·implementation-dependent·
The rules governing the mapping from JSON to XML are as follows. In these rules, the phrase
"an element named N" is to be interpreted as meaning "an element node whose local name is N and whose
namespace URI is http://www.w3.org/2005/xpath-functions
".
The JSON value null
is represented by an element named null
, with empty content.
The JSON values true
and false
are represented by an element named boolean
,
with content conforming to the type xs:boolean
. When the element is created by the
fn:json-to-xml
function, the string value of the element will be true
or false
.
The fn:xml-to-json
function also recognizes other strings that validate as xs:boolean
,
for example 1
and 0
. Leading and trailing whitespace is accepted.
A JSON number is represented by an element named number
,
with content conforming to the type xs:double
, with the additional restriction that the value
must not be positive or negative infinity, nor NaN
. The
fn:json-to-xml
function creates an element whose string value is lexically the same as the JSON representation
of the number. The fn:xml-to-json
function generates a JSON representation that is the result of casting the
(typed or untyped) value of the node to xs:double
and then casting the result to xs:string
.
Leading and trailing whitespace is accepted.
Since JSON does not impose limits on the range or precision
of numbers, these rules mean that conversion from JSON to XML will always succeed, and will retain full precision
in the lexical representation unless the data model implementation is one that reconstructs the string value from
the typed value. In the reverse direction, conversion from XML to JSON may fail if the value is infinity or NaN
,
or if the string value is such that casting to xs:double
produces positive or negative infinity.
A JSON string is represented by an element named string
, with
content conforming to the type xs:string
. The string
element has two
alternative representations: escaped form, and unescaped form.
A JSON array is represented by an element named array
. The content is a sequence of
child elements representing the members of the array in order, each such element being the representation
of the array member obtained by applying these rules recursively.
A JSON object is represented by an element named map
. The content is a sequence
of child elements each of which represents one of the name/value pairs in the object. The representation of the
name/value pair N:V is obtained by taking the element that represents the value V (by applying these
rules recursively) and adding an attribute with name key
(in no namespace), whose
value is N as an instance of xs:string
. The functions fn:json-to-xml
and
fn:xml-to-json
both retain the order of entries, subject to rules about how duplicate keys are handled. The
key may be represented in escaped or unescaped form.
The attribute escaped="true"
may be specified on a string
element to indicate
that the string value contains backslash-escaped characters that are to be interpreted according to the JSON
rules. The attribute escaped-key="true"
may be specified on any element with a key
attribute to indicate
that the key contains backslash-escaped characters that are to be interpreted according to the JSON
rules. Both attributes have the default value false
, signifying that the relevant value is in unescaped form.
In unescaped form, the backslash character has no special significance (it represents itself).
The JSON grammar for number
is a subset of the lexical space of
the XSD type xs:double
. The mapping from JSON number
values to xs:double
values is defined by the XPath rules for casting from xs:string
to xs:double
. Note that
these rules will never generate an error for out-of-range values; instead very large or very small values will be
converted to +INF
or -INF
. Since JSON does not impose limits on the range or precision
of numbers, the conversion is not guaranteed to retain full precision.
Although the order of entries in a JSON object is generally considered to have no significance, the functions
json-to-xml
and json-to-xml
both retain order.
The XDM representation of a JSON value may either be untyped (all elements annotated as xs:untyped
, attributes
as xs:untypedAtomic
), or it may be typed. If it is typed, then it must have the type
annotations obtained by validating the untyped representation against the schema given in C.2 Schema for the result of fn:json-to-xml.
If it is untyped, then it must be an XDM instance such that validation against this schema would succeed;
with the proviso that all attributes other than those in no namespace or in namespace http://www.w3.org/2005/xpath-functions
are ignored, including attributes such as xsi:type
and xsi:nil
that would normally influence the process
of schema validation.
The namespace prefix associated with the namespace http://www.w3.org/2005/xpath-functions
(if any) is immaterial.
The effect of the fn:xml-to-json
function does not depend on the choice of prefix, and the prefix (if any) generated by the
fn:json-to-xml
function is ·implementation-dependent·.
The functions listed parse or serialize JSON data.
Function | Meaning |
---|---|
fn:parse-json |
Parses a string supplied in the form of a JSON text, returning the results typically in the form of a map or array. |
fn:json-doc |
Reads an external resource containing JSON, and returns the result of parsing the resource as JSON. |
fn:json-to-xml |
Parses a string supplied in the form of a JSON text, returning the results in the form of an XML document node. |
fn:xml-to-json |
Converts an XML tree, whose format corresponds to the XML representation of JSON defined in this specification, into a string conforming to the JSON grammar. |
fn:json |
Creates a JSON representation of an arbitrary XDM value. |
Note also that the function fn:serialize
has an option to act as the inverse function to fn:parse-json
.
Parses a string supplied in the form of a JSON text, returning the results typically in the form of a map or array.
fn:parse-json ( |
||
$json |
as xs:string? |
|
) as item()? |
fn:parse-json ( |
||
$json |
as xs:string? , |
|
$options |
as map(*) |
|
) as item()? |
This function is ·deterministic·, ·context-independent·, and ·focus-independent·.
The effect of the one-argument form of this function is the same as calling the
two-argument form with an empty map as the value of the $options
argument.
The first argument is a JSON text as defined in [RFC 7159], in the form of a string. The function parses this string to return an XDM value.
If $json
is the empty sequence, the function returns the empty sequence.
Note:
The result will also be an empty sequence if $json
is the string "null"
.
The $options
argument can be used to control the way in which the parsing
takes place. The ·option parameter conventions· apply.
The entries that may appear in the $options
map are as follows:
Key | Value | Meaning |
---|---|---|
liberal |
Determines whether deviations from the syntax of RFC7159 are permitted.
|
|
false |
The input must consist of an optional byte order mark (which is ignored) followed by a string
that conforms to the grammar of JSON-text in [RFC 7159]. An error must be raised
[err:FOJS0001] if the input does not conform to the grammar.
|
|
true |
The input may contain deviations from the grammar of [RFC 7159], which are handled in an ·implementation-defined· way. (Note: some popular extensions include allowing quotes on keys to be omitted, allowing a comma to appear after the last item in an array, allowing leading zeroes in numbers, and allowing control characters such as tab and newline to be present in unescaped form.) Since the extensions accepted are implementation-defined, an error may be raised [err:FOJS0001] if the input does not conform to the grammar. | |
duplicates |
Determines the policy for handling duplicate keys in a JSON object.
To determine whether keys are duplicates, they are compared using the Unicode codepoint
collation, after expanding escape
sequences, unless the escape option is set to true , in which
case keys are compared in escaped form.
|
|
reject |
An error is raised [err:FOJS0003] if duplicate keys are encountered. | |
use-first |
If duplicate keys are present in a JSON object, all but the first of a set of duplicates are ignored. | |
use-last |
If duplicate keys are present in a JSON object, all but the last of a set of duplicates are ignored. | |
escape |
Determines whether special characters are represented in the XDM output in backslash-escaped
form.
|
|
false |
All characters in the input that are valid
in the version of XML supported by the implementation, whether or not they are represented
in the input by means of an escape sequence, are represented as unescaped characters
in the result. Any
characters or codepoints that are not valid XML characters
(for example, unpaired surrogates) are passed to the fallback function
as described below; in the absence of a fallback function, they are replaced by
the Unicode REPLACEMENT CHARACTER (xFFFD ).
|
|
true |
JSON escape sequences are used in the result to represent special characters in the
JSON input, as defined below,
whether or not they were represented using JSON escape sequences in the input.
The characters that are considered "special" for this purpose are:
\t ), or a six-character escape sequence otherwise
(for example \uDEAD ). Characters other than these are not escaped in the result, even if they
were escaped in the input.
|
|
fallback |
Provides a function which is called when the input contains an escape sequence
that represents a character that is not valid in the version of XML
supported by the implementation.
It is an error to supply the fallback option if the escape option is present
with the value true .
|
|
User-supplied function |
The function is called when the JSON input contains a special character (as defined
under
the escape option) that is valid according to the JSON
grammar, whether the special character is represented in the input directly or as
an escape sequence.
The function is called once for any surrogate
that is not properly paired with another surrogate. The string supplied as the argument
will always be a two- or six- character escape
sequence, starting with a backslash, that conforms to the rules in the JSON grammar
(as extended by the
implementation if liberal:true() is specified): for example
\b or \uFFFF or \uDEAD . The function is not
called for an escape sequence that is invalid against the grammar (for example \x0A ). The function returns a string
which is inserted into the result in place of the invalid character. The
function also has the option of raising a dynamic error by calling fn:error .
|
|
number-parser |
Determines how numeric values should be processed.
|
|
User-supplied function |
The supplied function is called to process the string value of any JSON number
in the input. By default, numbers are processed by
converting to xs:double using the XPath casting rules.
Supplying the value xs:decimal#1 will instead convert to xs:decimal
(which potentially retains more precision, but disallows exponential notation), while
supplying a function that casts to union(xs:decimal, xs:double) will treat
the value as xs:decimal if there is no exponent, or as xs:double
otherwise. Supplying the value fn:identity#1 causes the value to be retained
unchanged as an xs:string . Before calling the supplied number-parser ,
the value is first checked to ensure that it conforms to the JSON grammar (for example,
a leading plus sign and redundant leading zeroes are not allowed); these checks are
disabled
if the liberal option is set to true .
|
|
() |
If no function is supplied, numbers are processed by casting the supplied value to
xs:double .
|
The various structures that can occur in JSON are transformed recursively to XDM values as follows:
A JSON object is converted to a map.
The entries in the map correspond to the key/value
pairs in the JSON object. The key is always of type xs:string
; the
associated value may be of any type, and is the result of converting the JSON
value by recursive application of these rules. For example, the JSON text
{"x":2, "y":5}
is transformed to the value map{"x":2,
"y":5}
.
If duplicate keys are encountered in a JSON object, they are handled
as determined by the duplicates
option defined above.
A JSON array is transformed to an array whose members are the result of converting
the corresponding member of the array by recursive application of these rules. For
example, the JSON text ["a", "b", null]
is transformed to the value
["a", "b", ()]
.
A JSON string is converted to an xs:string
value.
The handling of special characters depends on the
escape
and fallback
options, as described in the table above.
A JSON number is processed using the function supplied
in the number-parser
option; by default it is converted to an xs:double
value using
the rules for casting from xs:string
to xs:double
.
The JSON boolean values true
and false
are
converted to the corresponding xs:boolean
values.
The JSON value null is converted to the empty sequence.
A dynamic error [err:FOJS0001] occurs if the value of
$json
does not conform to the JSON grammar, unless the option
"liberal":true()
is present and the processor chooses to accept the deviation.
A dynamic error [err:FOJS0003] occurs if the option
"duplicates":"reject"
is present and the value of
$json
contains a JSON object with duplicate keys.
A dynamic error [err:FOJS0005] occurs if the $options
map contains an entry whose key is defined in this specification and whose value is not valid for that key,
or if it contains an entry with the key fallback
when the option "escape":true()
is also present.
The result of the function will be an instance of one of the following types. An
instance of
test (or in XQuery, typeswitch
) can be used to
distinguish them:
map(xs:string, item()?)
for a JSON object
array(item()?)
for a JSON array
xs:string
for a JSON string
xs:double
for a JSON number
xs:boolean
for a JSON boolean
empty-sequence()
for a JSON null (or for empty input)
If the input starts with a byte order mark, this function ignores it. The byte order mark may have been added to the data stream in order to facilitate decoding of an octet stream to a character string, but since this function takes a character string as input, the byte order mark serves no useful purpose.
The possibility of the input containing characters that are not valid in XML (for example, unpaired surrogates)
arises only when such characters are expressed using JSON escape sequences. The is because the input to the function
is an instance of xs:string
, which by definition can only contain characters that are valid in XML.
The expression parse-json('{"x":1, "y":[3,4,5]}')
returns map{"x":1e0,"y":[3e0,4e0,5e0]}
.
The expression parse-json('"abcd"')
returns "abcd"
.
The expression parse-json('{"x":"\\", "y":"\u0025"}')
returns map{"x":"\","y":"%"}
.
The expression parse-json('{"x":"\\", "y":"\u0025"}', map{'escape':true()})
returns map{"x":"\\","y":"%"}
.
The expression parse-json('{"x":"\\", "y":"\u0000"}')
returns map{"x":"\","y":codepoints-to-string(65533)}
.
The expression parse-json('{"x":"\\", "y":"\u0000"}', map{'escape':true()})
returns map{"x":"\\","y":"\u0000"}
.
The expression parse-json('{"x":"\\", "y":"\u0000"}', map{'fallback':function($s){'['||$s||']'}})
returns map{"x":"\","y":"[\u0000]"}
.
Reads an external resource containing JSON, and returns the result of parsing the resource as JSON.
fn:json-doc ( |
||
$href |
as xs:string? |
|
) as item()? |
fn:json-doc ( |
||
$href |
as xs:string? , |
|
$options |
as map(*) |
|
) as item()? |
This function is ·deterministic·, ·context-dependent·, and ·focus-independent·. It depends on static base URI.
The effect of the single-argument call fn:json-doc($H)
is the same as the effect of the two-argument call
fn:json-doc($H, map{})
where an empty map is supplied as the second argument.
The effect of the two-argument function call fn:json-doc($H, $M)
is equivalent to the function composition
fn:unparsed-text($H) => fn:parse-json($M)
; except that:
The function may accept a resource in any encoding. [RFC 7159] requires UTF-8, UTF-16, or UTF-32 to be accepted, but it is not an error if a different encoding is used. Unless external encoding information is available, the function must assume that the encoding is one of UTF-8, UTF-16, or UTF-32, and must distinguish these cases by examination of the initial octets of the resource.
If the resource contains characters that are not valid in the version of XML used by the processor,
then rather than raising an error as fn:unparsed-text#1
does, the function replaces such characters by the equivalent
JSON escape sequence prior to parsing.
Note:
Equivalently, the implementation can use some other internal representation of strings that allows non-XML characters to be manipulated.
If $href
is the empty sequence, the function returns the empty sequence.
The function may raise any error defined for the fn:unparsed-text
or fn:parse-json
functions.
If the input cannot be decoded (that is, converted into a sequence of Unicode codepoints, which may or may not represent characters),
then a dynamic error occurs as with the fn:unparsed-text
function.
If the input can be decoded,
then the possibility still arises that the resulting sequence of codepoints includes codepoints that do not represent characters that are valid in the
version of XML that the processor supports. Such codepoints are translated into JSON escape sequences (for example, \uFFFF
),
and the JSON escape sequence is then passed to the fallback function specified in the $options
argument, which in turn
defaults to a function that returns the Unicode REPLACEMENT CHARACTER
(xFFFD
).
Parses a string supplied in the form of a JSON text, returning the results in the form of an XML document node.
fn:json-to-xml ( |
||
$json |
as xs:string? |
|
) as document-node()? |
fn:json-to-xml ( |
||
$json |
as xs:string? , |
|
$options |
as map(*) |
|
) as document-node()? |
This function is ·nondeterministic·, ·context-dependent·, and ·focus-independent·. It depends on static base URI.
The effect of the one-argument form of this function is the same as calling the
two-argument form with an empty map as the value of the $options
argument.
The first argument is a JSON-text as defined in [RFC 7159], in the form of a string. The function parses this string to return an XDM node.
If $json
is an empty sequence, the function returns the empty sequence.
The $options
argument can be used to control the way in which the parsing
takes place. The ·option parameter conventions· apply.
The entries that may appear in the $options
map are as follows:
Key | Value | Meaning |
---|---|---|
liberal |
Determines whether deviations from the syntax of RFC7159 are permitted.
|
|
false |
The input must consist of an optional byte order mark (which is ignored) followed by a string
that conforms to the grammar of JSON-text in [RFC 7159]. An error must be raised
(see below) if the input does not conform to the grammar.
|
|
true |
The input may contain deviations from the grammar of [RFC 7159], which are handled in an ·implementation-defined· way. (Note: some popular extensions include allowing quotes on keys to be omitted, allowing a comma to appear after the last item in an array, allowing leading zeroes in numbers, and allowing control characters such as tab and newline to be present in unescaped form.) Since the extensions accepted are implementation-defined, an error may be raised (see below) if the input does not conform to the grammar. | |
duplicates |
Determines the policy for handling duplicate keys in a JSON object.
To determine whether keys are duplicates, they are compared using the Unicode codepoint
collation, after expanding escape
sequences, unless the escape option is set to true , in which
case keys are compared in escaped form.
|
|
reject |
An error is raised [err:FOJS0003] if duplicate keys are encountered. | |
use-first |
If duplicate keys are present in a JSON object, all but the first of a set of duplicates are ignored. | |
retain |
If duplicate keys are present in a JSON object, the XML result of the function will
also contain duplicates (making
it invalid against the schema). This value is therefore incompatible with the option
validate=true
[err:FOJS0005]
|
|
validate |
Determines whether the generated XML tree is schema-validated.
|
|
true |
Indicates that the resulting XDM instance must be typed; that is, the element
and attribute nodes must carry the type annotations that result from validation
against the schema given at C.2 Schema for the result of fn:json-to-xml, or against an
·implementation-defined· schema
if the liberal option has the value true .
|
|
false |
Indicates that the resulting XDM instance must be untyped. | |
escape |
Determines whether special characters are represented in the XDM output
in backslash-escaped form.
|
|
false |
All characters in the input that are valid
in the version of XML supported by the implementation, whether or not they are represented
in the input by means of an escape sequence, are represented as unescaped characters
in the result. Any
characters or codepoints that are not valid XML characters
(for example, unpaired surrogates) are passed to the fallback function
as described below; in the absence of a fallback function, they are replaced by
the Unicode REPLACEMENT CHARACTER (xFFFD ).
The attributes escaped and escaped-key will not be present in the XDM output.
|
|
true |
JSON escape sequences are used in the result to represent special characters in the
JSON input, as defined below,
whether or not they were represented using JSON escape sequences in the input.
The characters that are considered "special" for this purpose are:
\t ), or a six-character escape sequence otherwise
(for example \uDEAD ). Characters other than these will not be escaped in the result,
even if they were escaped in the input. In the result:
|
|
fallback |
Provides a function which is called when the input contains an escape sequence
that represents a character that is not valid in the version of XML
supported by the implementation.
It is an error to supply the fallback option if the escape option is present
with the value true .
|
|
User-supplied function |
The function is called when the JSON input contains an escape sequence that is valid
according to the JSON
grammar, but which does not represent a character that is valid in the version of
XML supported
by the processor. In the case of surrogates, the function is called once for any six-character
escape sequence
that is not properly paired with another surrogate. The string supplied
as the argument will always be a two- or six- character escape
sequence, starting with a backslash, that conforms to the rules in the JSON grammar
(as extended by the
implementation if liberal:true() is specified): for example
\b or \uFFFF or \uDEAD . The function is not
called for an escape sequence that is invalid against the grammar (for example \x0A ).
The function returns a string
which is inserted into the result in place of the invalid character. The
function also has the option of raising a dynamic error by calling fn:error .
|
The various structures that can occur in JSON are transformed recursively to XDM values according to the rules given in 17.4.2 XML Representation of JSON.
The function returns a document node, whose only child is the element node representing the outermost construct in the JSON text.
The function is ·non-deterministic with respect to node identity·: that is, if the function is called twice with the same arguments, it is ·implementation-dependent· whether the same node is returned on both occasions.
The base URI of the returned document node is taken from the static base URI of the function call.
The choice of namespace prefix (or absence of a prefix) in the names of constructed nodes is ·implementation-dependent·.
The XDM tree returned by the function does not contain any
unnecessary (albeit valid) nodes such as whitespace text nodes, comments, or processing instructions.
It does not include any whitespace in the value of number
or boolean
element nodes, or in the value of escaped
or escaped-key
attribute nodes.
If the result is typed, every element named string
will have an attribute named
escaped
whose value is either true
or false
, and every element having
an attribute named key
will also have an attribute named escaped-key
whose value is either
true
or false
.
If the result is untyped, the attributes escaped
and escaped-key
will
either be present with the value true
, or will be absent. They will never be present with the value false
.
An error is raised [err:FOJS0001] if the value of
$json
does not conform to the JSON grammar as defined
by [RFC 7159], unless the option "liberal":true()
is present and
the processor chooses to accept the deviation.
An error is raised [err:FOJS0004] if the value of
the validate
option is true
and the processor does not support
schema validation or typed data.
An 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.
To read a JSON file, this function can be used in conjunction with the
fn:unparsed-text
function.
Many JSON implementations allow commas to be used after the last item in an object or
array, although the specification does not permit it. The option
spec="liberal"
is provided to allow such deviations from the
specification to be accepted. Some JSON implementations also allow constructors such as
new Date("2000-12-13")
to appear as values: specifying
spec="liberal"
allows such extensions to be accepted, but does not
guarantee it. If such extensions are accepted, the resulting value is
implementation-defined, and will not necessarily conform to the schema at C.2 Schema for the result of fn:json-to-xml.
If the input starts with a byte order mark, this function ignores it. The byte order mark may have been added to the data stream in order to facilitate decoding of an octet stream to a character string, but since this function takes a character string as input, the byte order mark serves no useful purpose.
The possibility of the input containing characters that are not valid in XML (for example, unpaired surrogates)
arises only when such characters are expressed using JSON escape sequences. The is because the input to the function
is an instance of xs:string
, which by definition can only contain characters that are valid in XML.
The expression json-to-xml('{"x": 1, "y": [3,4,5]}')
returns (with whitespace added for legibility):
<map xmlns="http://www.w3.org/2005/xpath-functions"> <number key="x">1</number> <array key="y"> <number>3</number> <number>4</number> <number>5</number> </array> </map>
The expression json-to-xml('"abcd"', map{'liberal': false()})
returns <string xmlns="http://www.w3.org/2005/xpath-functions">abcd</string>
.
The expression json-to-xml('{"x": "\\", "y": "\u0025"}')
returns (with whitespace added for legibility):
<map xmlns="http://www.w3.org/2005/xpath-functions"> <string key="x">\</string> <string key="y">%</string> </map>
The expression json-to-xml('{"x": "\\", "y": "\u0025"}', map{'escape':
true()})
returns (with whitespace added for legibility):
<map xmlns="http://www.w3.org/2005/xpath-functions"> <string escaped="true" key="x">\\</string> <string key="y">%</string> </map>
The following example illustrates use of the fallback
function to
handle characters that are invalid in XML.
let $jsonstr := unparsed-text('http://example.com/endpoint'), $options := map { 'liberal': true(), 'fallback': function($char as xs:string) as xs:string { let $c0chars := map { '\u0000':'[NUL]', '\u0001':'[SOH]', '\u0002':'[STX]', ... '\u001E':'[RS]', '\u001F':'[US]' }, $replacement := $c0chars($char) return if (exists($replacement)) then $replacement else error(xs:QName('err:invalid-char'), 'Error: ' || $char || ' is not a C0 control character.') } } return json-to-xml($jsonstr, $options)
Converts an XML tree, whose format corresponds to the XML representation of JSON defined in this specification, into a string conforming to the JSON grammar.
fn:xml-to-json ( |
||
$node |
as node()? |
|
) as xs:string? |
fn:xml-to-json ( |
||
$node |
as node()? , |
|
$options |
as map(*) |
|
) as xs:string? |
This function is ·deterministic·, ·context-independent·, and ·focus-independent·.
The effect of the one-argument form of this function is the same as calling the
two-argument form with an empty map as the value of the $options
argument.
The first argument $node
is a node; the subtree rooted at this node will typically be
the XML representation of a JSON document as defined in 17.4.2 XML Representation of JSON.
If $node
is the empty sequence, the function returns the empty sequence.
The $options
argument can be used to control the way in which the conversion
takes place. The ·option parameter conventions· apply.
The entries that may appear in the $options
map are as follows:
Key | Value | Meaning |
---|---|---|
indent |
Determines whether additional whitespace should be added to the output to improve
readability.
|
|
false |
The processor must not insert any insignificant whitespace between JSON tokens. | |
true |
The processor may insert whitespace between JSON tokens in order to improve readability. The specification imposes no constraints on how this is done. | |
number-formatter |
Determines how numeric values should be formatted.
|
|
User-supplied function |
The supplied function is called to process the string value of all fn:number
elements in the input. For example, setting the value to fn:identity#1
causes the value to be output unchanged. There is no requirement that the result should
be valid JSON.
|
|
() |
If no function is supplied, numbers are formatted by converting the string value of
the fn:number element to an xs:double , and then converting
the result to a string using the casting rules. Note that this will result in exponential
notation being used for values outside the range 1e-6 to 1e+6. A dynamic error occurs
for values such as infinity and NaN where the resulting JSON would be invalid.
|
The node supplied as $node
must be one of the following: [err:FOJS0006]
An element node whose name matches the name of a global element declaration in the schema given in C.2 Schema for the result of fn:json-to-xml ("the schema") and that is valid as defined below:
If the type annotation of the element matches the type of the relevant element declaration in the schema (indicating that the element has been validated against the schema), then the element is considered valid.
Otherwise, the processor may attempt to validate the element against the schema, in which case it is treated as valid if and only if the outcome of validation is valid.
Otherwise (if the processor does not attempt validation using the schema),
the processor must ensure that the content of the element,
after stripping all attributes (at any depth) in namespaces other than
http://www.w3.org/2005/xpath-functions
, is such that validation
against the schema would have an outcome of valid.
Note:
The process described here is not precisely equivalent to schema validation.
For example, schema validation will fail if there is an invalid xsi:type
or xsi:nil
attribute, whereas this process will ignore such attributes.
An element node E having a key
attribute and/or an escaped-key
attribute
provided that E would satisfy one of the above
conditions if the key
and/or escaped-key
attributes were removed.
A document node having exactly one element child and no text node children, where the element child satisfies one of the conditions above.
Furthermore, $node
must satisfy the following constraint
(which cannot be conveniently expressed in the schema). Every element M that is a descendant-or-self of
$node
and has local name map
and namespace URI http://www.w3.org/2005/xpath-functions
must satisfy the following rule: there must not be two distinct children of M (say C1 and C2)
such that the normalized key of C1 is equal to the normalized key of C2. The normalized key
of an element C is as follows:
If C has the attribute value escaped-key="true"
, then the value of the
key
attribute of C, with all JSON escape sequences replaced by the corresponding Unicode characters
according to the JSON escaping rules.
Otherwise (the escaped-key
attribute of C is absent or set to false),
the value of the key
attribute of C.
Nodes in the input tree are handled by applying the following rules, recursively. In these rules the term
"an element named N" means "an element node whose local name is N and whose namespace URI is
http://www.w3.org/2005/xpath-functions
".
A document node having a single element node child is processed by processing that child.
An element named null
results in the output null
.
An element $E
named boolean
results in the output true
or false
depending on the result of xs:boolean(fn:string($E))
.
An element $E
named number
is processed as follows:
If the number-formatter
option is present and non-empty,
the supplied function is called, with the string value of $E
as its argument,
and the result is output (whether or not it is valid JSON).
Otherwise, the result of the expression xs:string(xs:double(fn:string($E)))
is output.
Note:
The default formatting results in exponential format being used for numbers whose
absolute value is outside the range 1e-6 to 1e+6; although this is valid according to the JSON
specification, some receiving applications may be unable to process it. Possible reasons for using
a number-formatter
might be:
To avoid use of exponential notation in the output.
To avoid loss of precision when the numbers to be output have
greater precision than an xs:double
.
To improve the human readability of the output, for example by calling fn:format-number
to limit the number of decimal places in the result.
To avoid errors when dealing with values that JSON cannot handle, such as Infinity and NaN (for example, by emitting these as strings within quotation marks).
An element named string
results in the output of the string value of the element, enclosed in
quotation marks, with any special characters in the string escaped as described below.
An element named array
results in the output of the children of the array
element,
each processed by applying these rules recursively: the items in the resulting list are enclosed between square brackets,
and separated by commas.
An element named map
results in the output of a sequence of map entries corresponding to
the children of the map
element, enclosed between curly braces and separated by commas.
Each entry comprises the value of the key
attribute of the child element, enclosed in quotation marks
and escaped as described below, followed by a colon, followed by the result of processing the child element
by applying these rules recursively.
Comments, processing instructions, and whitespace text node children of map
and array
are ignored.
Strings are escaped as follows:
If the attribute escaped="true"
is present for a string value, or escaped-key="true"
for a key value, then:
any valid JSON escape sequence present in the string is copied unchanged to the output;
any invalid JSON escape sequence results in a dynamic error [err:FOJS0007];
any unescaped occurrence of quotation mark, backspace, form-feed, newline, carriage return, tab, or solidus is replaced by
\"
, \b
, \f
, \n
, \r
, \t
, or \/
respectively;
any other codepoint in the range 1-31 or 127-159 is replaced by an escape in the form \uHHHH where HHHH is the upper-case hexadecimal representation of the codepoint value.
Otherwise (that is, in the absence of the attribute escaped="true"
for a string value,
or escaped-key="true"
for a key value):
any occurrence of backslash is replaced by \\
any occurrence of quotation mark, backspace, form-feed, newline, carriage return, or tab is
replaced by \"
, \b
, \f
, \n
, \r
, or \t
respectively;
any other codepoint in the range 1-31 or 127-159 is replaced by an escape in
the form \uHHHH
where HHHH
is the upper-case hexadecimal representation of the codepoint value.
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.
A dynamic error is raised [err:FOJS0006] if the value of
$node
is not a document or element node or is not valid according to the schema for the XML representation of
JSON, or if a map
element has two children whose normalized key values are the same.
A dynamic error is raised [err:FOJS0007] if the value of
$node
includes a string labeled with escaped="true"
, or
a key labeled with escaped-key="true"
, where the content of the string or key
contains an invalid JSON escape sequence: specifically, where it contains a backslash (\
) that is not followed by one
of the characters "
, \
, /
, b
, f
, n
,
r
, t
, or u
, or where it contains the characters \u
not followed by four hexadecimal digits (that is [0-9A-Fa-f]{4}
).
The rule requiring schema validity has a number of consequences, including the following:
The input cannot contain no-namespace attributes, or attributes in the namespace http://www.w3.org/2005/xpath-functions
,
except where explicitly allowed by the schema. Attributes in other namespaces, however, are ignored.
Nodes that do not affect schema validity, such as comments, processing instructions, namespace nodes, and whitespace text node
children of map
and array
, are ignored.
Numeric values are restricted to those that are valid in JSON: the schema disallows positive and negative infinity and NaN.
Duplicate key values are not permitted. Most cases of duplicate keys are prevented by the rules in the schema;
additional cases (where the keys are equal only after expanding JSON escape sequences) are prevented by the prose rules
of this function. For example, the key values \n
and \u000A
are treated as duplicates even though
the rules in the schema do not treat them as such.
The rule allowing the top-level element to have a key
attribute (which is ignored)
allows any element in the output of the fn:json-to-xml
function
to be processed: for example, it is possible to take a JSON document, convert it to XML, select
a subtree based on the value of a key
attribute, and then convert this subtree
back to JSON, perhaps after a transformation. The rule means that an element with the appropriate name will be
accepted if it has been validated against one of the
types mapWithinMapType
, arrayWithinMapType
, stringWithinMapType
,
numberWithinMapType
, booleanWithinMapType
, or nullWithinMapType
.
The input <array xmlns="http://www.w3.org/2005/xpath-functions"><number>1</number><string>is</string><boolean>1</boolean></array>
produces the result [1,"is",true]
.
The input <map xmlns="http://www.w3.org/2005/xpath-functions"><number key="Sunday">1</number><number key="Monday">2</number></map>
produces the result {"Sunday":1,"Monday":2}
.
Creates a JSON representation of an arbitrary XDM value.
fn:json ( |
||
$input |
as item()* |
|
) as xs:string |
fn:json ( |
||
$input |
as xs:string? , |
|
$options |
as map(*) |
|
) as xs:string |
This function is ·deterministic·, ·context-independent·, and ·focus-independent·.
This function returns a string, in JSON format, containing a representation of the
supplied input $input
. The function is error-free (it accepts any input sequence
whatsoever), but it is not lossless: there are cases when two different XDM values will
have the same JSON representation. For example, the sequence (1, 2)
and the array [1, 2]
are both output as [1,2]
.
The entries that may appear in the $options
map are as follows:
Key | Value | Meaning |
---|---|---|
indent |
Determines whether additional whitespace should be added to the output to improve
readability.
|
|
false |
The processor must not insert any insignificant whitespace between JSON tokens. | |
true |
The processor may insert whitespace between JSON tokens in order to improve readability. The specification imposes no constraints on how this is done. | |
element-map |
Determines whether elements whose children are element nodes with distinct
names should be treated specially.
|
|
false |
The processor treats such elements in the same way as any other element. | |
true |
The processor generates a JSON object in which the child element names are used as JSON property names. |
An input sequence is handled as follows:
An empty sequence is output as the JSON value null.
A singleton sequence is output following the rules for processing items, below.
A sequence of two or more items results in a JSON array, whose members are constructed from the items by applying the rules below.
Items are processed as follows:
Atomic values
An xs:boolean
value is output as the JSON value true
or false
.
A numeric value, other than INF
, -INF
, or NaN
,
is output as a JSON number.
Any other atomic value is cast to xs:string
, and the result is output as a JSON string,
escaped as described below.
Nodes
Document nodes
A document node is output as a JSON object with two properties, in order:
A property #document
set to the value of the base URI of the document
if available, or an empty string otherwise..
A property #content
whose value follows the rules for
outputting the content of an element node, given below.
Element nodes
An element node is output as a JSON object with the following properties, in order:
#element
set to the local name of the element.
If the element name has a prefix, #prefix
, set to the value of the prefix.
If the element name is in a namespace, #namespace
, set to the value of the
namespace URI.
For each attribute of the element, in arbitrary order, a property whose name is derived from the attribute name as follows:
If the attribute name is in no namespace, then "@"
followed
by the local name.
If the attribute name is in the XML namespace, then "@xml:"
followed
by the local name.
Otherwise "@Q{uri}local"
where uri
is the namespace
URI and local
is the local name.
The property value is the result of atomizing the attribute node and applying the
fn:json
function to the result. (For untyped attributes, the result
will always be a single string.)
The children of the element are processed as follows:
If there are no children, nothing is output.
If the element has a type annotation that is a simple type, or if its content
comprises a single text node, then a property #value
set to the result
of atomizing the element node and applying the fn:json
function
to the result.
If (a) the children consist exclusively of elements and whitespace-only
text nodes, and (b) the child element nodes are all in the same namespace, or all in no
namespace, and (c) each child element has a local name that is distinct from the local
name of any other child, and (d) the element-map
option is not
present in $options
with the value false()
,
then a property #content
whose value is a JSON
object having one property for each child element node. The name of this property
is the local name of the element, and the value of the property is obtained by applying
these rules recursively, except that for an empty element, the value is represented
as JSON null
.
Otherwise, a property #content
whose value is an array, with
one member for each child node (including whitespace-only text nodes), obtained
by applying the fn:json
function to that child node.
Text nodes
A JSON object with a single property #text
whose value is the
string value of the text node.
Comment nodes
A JSON object with a single property #comment
whose value is the
string value of the comment.
Processing instruction nodes
A JSON object with a two properties (in order): #processing-instruction
set to the
name of the processing instruction, and #data
set to the
string value of the processing instruction node.
Attribute nodes
Attribute nodes that are reached via an element node are output as described under "element nodes", above.
Free-standing attribute nodes are output as JSON objects with properties
#attribute
set to the local name of the attribute, #prefix
(if non-empty) set to the prefix of the attribute's name, #namespace
(if non-empty) set to the namespace URI, and #value
set to
the result of atomizing the attribute value and applying the
fn:json
function to the result.
Namespace nodes
Namespace nodes that are reached via an element node result in no output.
Free-standing namespace nodes are output as JSON objects with properties
#namespace
set to the namespace prefix (""
for the default
namespace) and #uri
set to the namespace URI.
Maps
An XDM map is output as a JSON object with one property for each entry in the map.
The property name is derived from the key value by converting the value to a string
and applying escaping rules. If the property name thus generated is the same as a previously output
property name, then it is made unique by appending "(N)"
where N is the smallest
positive integer that makes the resulting value unique.
The property value is derived by applying the fn:json
function to the value in the map entry.
Note:
Conflicts between property names can arise because the XDM model allows keys of different types,
for example the xs:date
value 2020-12-31
and the string value
"2020-12-31"
can co-exist. The map map{xs:duration('PT1D'):20, "PT1D":30}
is converted to the JSON string {"PT1D":20,"PT1D(1)":30}
or
{"PT1D":30,"PT1D(1)":20}
, depending on the (unpredictable) order in which the
entries in the map are processed.
Note:
Because the order of entries in a map is unpredictable, the order in which the properties are listed in the JSON output is also unpredictable.
Arrays
An XDM array is output as a JSON array. Each member of the XDM array generates one entry in the
JSON array, in order, obtained by applying the fn:json
function to the XDM array member.
Functions
An XDM function, other than a map or array, is output as a JSON object with the following properties:
#function
, set to the local name of the function
if it has a name, or the empty string otherwise.
#namespace
, set to the namespace URI of the function. The property
is omitted for an anonymous function.
#arity
, set to the arity of the function as a JSON number.
#arguments
whose value is an array
of strings, which identify the names and types of the function arguments,
in the format $Q{uri}local as SequenceType
: for example
["$x as double", "$y as string"]
. Namespace prefixes must not be used:
unprefixed element names and variable names are taken to be in no namespace, and unprefixed
type names are taken to be in the namespace http://www.w3.org/2001/XMLSchema
.
#result
whose value is a string
identifying the type of the function result, using the same conventions as for #arguments
.
Optionally at implementer discretion, #implementation
whose value is a string
representing the function's implementation in implementation-defined format.
Strings are escaped as follows:
Any occurrence of backslash is replaced by \\
Any occurrence of quotation mark, backspace, form-feed, newline, carriage return, or tab is
replaced by \"
, \b
, \f
, \n
, \r
, or \t
respectively;
Any other codepoint in the range 1-31 or 127-159 is replaced by an escape in
the form \uHHHH
where HHHH
is the upper-case hexadecimal representation of the codepoint value.
In the JSON output, names of properties defined in this specification are prefixed with #
;
names not so prefixed are derived from names appearing in the input.
Namespace information may be lost (specifically, namespaces that are declared but not used are not retained in the output).
The distinction between sequences and arrays is lost.
The distinction between different atomic types is lost, except for the boolean / number / string distinction present in JSON.
In elements whose children are elements with distinct names, whitespace text nodes are lost, and the namespace URIs and prefixes of the child elements are lost.
The expression fn:json(())
returns 'null'
.
The expression fn:json(12)
returns '12'
.
The expression fn:json((12, "December"))
returns '[12,"December"]'
.
The expression fn:json(true())
returns 'true'
.
The expression fn:json(map{"a":1,"b":number('NaN'),"c":(1,2,3)})
returns '{"a":1,"b":"NaN","c":[1,2,3]}'
. ((or some permutation thereof)).
The expression fn:json(<a x="2">banana</a>)
returns '{"#element":"a","@x":"2","#value":"banana"}'
.
The expression fn:json(<a><b/><c>2</c></a>)
returns '{"#element":"a","#content":{"b":null,"c":"2"}}'
.
The expression fn:json(<a><b/><b/><c/></a>)
returns '{"#name":"a","#content":[{"#name":"b"},{"#name":"b"},{"#name":"c}]}'
.
The expression fn:json(<a>A <i>nice</i> one!</a>)
returns '{"#name":"a","#content":["A ",{"#name":"i", "#value":"nice"}," one!"]}'
.
Proposed for 4.0; not yet reviewed.