Maps
Maps were first introduced in XSLT 3.0 and in XPath 3.1. They correspond to the structures known as dictionaries or associative arrays in other languages, (or as objects in Javascript). There were two main motivations:
In streaming applications, it is not possible to visit nodes in a streamed XML document more than once. There is therefore a requirement for a flexible data structure that can be used to retain data that will be needed later in the processing.
Maps are needed for faithful representation of JSON input.
Once established for these purposes, maps found other uses, notably as a way of passing more complex options as function arguments, and for returning composite function results.
A map is a kind of XDM item (alongside nodes and atomic values). In fact, a map is a kind of function: you can think of it as a function defined extensionally (by tabulating the value of the function for all possible arguments) rather than intensionally (by means of an algorithm).
A map is a set of entries. Each entry is a key-value pair. The key is always an atomic item (it does not have to be a string). The value is any XDM value: a sequence of nodes, atomic item, functions, or maps.
Maps, like sequences, are immutable. When you add an entry to a map, you get a new map; the original is unchanged. Saxon provides an efficient implementation of maps that achieves this without copying the whole map every time an entry is added.
Since XPath 4.0, the entries in a map are ordered. This means that when a map is constructed using a map constructor expression, or by parsing JSON, the order of entries is preserved. Prior to 4.0, order was not significant. In Saxon 13, map order is preserved whether or not XPath 4.0 features are enabled.
Like sequences, maps do not have an intrinsic type of their own, but rather have a
type that can be inferred from what they contain. A map conforms to the type
map(K, V) if all the keys are of type K and all the values
are of type V. For example if the keys are all strings, and the values are
all employee elements, then the map conforms to the type
map(xs:string, element(employee)).
There are several ways to create a map:
-
If the number of entries is known, you can use the constructor syntax
map { key : value, key : value, ... }. Here the keys and values can be any "simple expression" (an expression not containing a top-level comma). If the keys and values are all known statically, you might write:map { "a" : 1, "e" : 2, "i" : 3, "o" : 4, "u" : 5 }. You can use this construct anywhere an XPath expression can be used, for example in theselectattribute of an xsl:variable element. -
The function map:merge() takes a number of maps as input, and combines them into a single map. This can be used to construct a map where the number of entries is not known statically: for example
map:merge(for $i in 49 to 57 return map{$i : codepoints-to-string($i)}). -
A single-entry map can also be constructed using the function
map:entry($key, $value). -
XPath 4.0 introduces the function map:build This constructs a map from an input sequence supplied as the first argument. The second argument is a function that computes the key for each input item; the third argument (if present) is a function that computes the corresponding value. For example,
map:build(//employee, fn{@ssn})constructs a map that indexes employee elements by the value of their@ssnattribute.
It is also possible to create maps using the XSLT 3.0 instructions xsl:map and xsl:map-entry. XSLT 4.0 introduces additional options to control the handling of duplicate keys.
Given a map $M, the value corresponding to a given key $K can
be found in several different ways:
By invoking the map as a function:
$M($K)By calling
map:get($M, $K)By using a lookup expression:
$M ? $KIn XPath 4.0, with a path expression:
$M/child::get($K)
Map functions
The summary of the full list of functions that operate on maps is as follows; for full
details see the Functions library. The
prefix map represents the namespace URI
http://www.w3.org/2005/xpath-functions/map.
-
map:build: returns a map that typically contains one entry for each item in a supplied input sequence. New in 4.0. -
map:contains: returnstrueif the given key is present in the map. Since XSLT 3.0 and XPath 3.1. -
map:empty: returnstrueif the supplied map contains no entries. New in 4.0. -
map:entries: returns a sequence containing all the key-value pairs present in a map, each represented as a single-entry map. New in 4.0. -
map:entry: creates a single-entry map that represents a single key-value pair. Useful as an argument tomap:merge(). Since XSLT 3.0 and XPath 3.1. -
map:filter: Selects entries from a map, returning a new map. New in 4.0. -
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. Since XSLT 3.0 and XPath 3.1. -
map:for-each: processes every key/value pair in a map by applying the given function. Prior to XPath 4.0, the results were returned as a sequence in unpredictable order; since XPath 4.0, the result is ordered. Since XSLT 3.0 and XPath 3.1. -
map:get: returns the value associated with the given key if present, or the empty sequence otherwise. Equivalent to calling$map($key). Since XSLT 3.0 and XPath 3.1. -
map:items: returns a sequence containing all the values present in a map, in order. New in 4.0. -
map:keys: returns the keys that are present in a map, in unpredictable order. Since XSLT 3.0 and XPath 3.1. -
map:keys-where: returns a sequence containing selected keys present in a map. New in 4.0. -
map:merge: takes a sequence of maps as input and combines them into a single map. Since XSLT 3.0 and XPath 3.1. -
map:put: adds an entry to a map, which replaces any existing entry for the same key, returning a new map. Since XSLT 3.0 and XPath 3.1. -
map:remove: removes an entry from a map (if it was present), returning a new map; if not present, returns the existing map unchanged. Since XSLT 3.0 and XPath 3.1. -
map:size: returns the number of entries (key/value pairs) in a map. Since XSLT 3.0 and XPath 3.1.
Changes in XPath 4.0
The main changes introduced for map items in XPath 4.0 and XSLT 4.0 are:
-
The items in a map are now ordered.
The main reason for this change is that serialized JSON results can easily become unreadable if the order of entries is randomized. The change also supports applications where the ordering carries semantic significance, for example a map whose keys are timestamps.
-
In a map constructor, the keyword
mapcan be omitted. So the expression{"x":1, "y":2}has the same effect asmap{"x":1, "y":2}. -
Various functions (
map:merge,map:build) and instructions (xsl:map) provide detailed control over how duplicate entries are handled. The values of the entries in a map can be bound to separate variables using a
letexpression. For example,let {$x, $y} := $map return ...is equivalent tolet $x := $map?x, $y := $map?y return ....-
A variant of the XPath
forexpression (or in XQuery, the FLWOR expression) is introduced to allow iterative processing of the entries in a map, in order: for examplestring-join(for key $k value $v return `{$k}="{$v}"`)produces a string representation of the map contents. (This example also uses the new XPath 4.0 construct of string templates). -
Trees of maps and arrays (called JTrees, made up of JNodes) can be processed using path expressions, in the same way as trees of XML nodes (now called XNodes when there is a need to make the distinction).
-
The type system allows maps whose keys are statically known to be described using a record type: for example
record(first, middle, last)describes the type of a map having three entries with keys"first","middle", and"last". -
Binary values of types
xs:hexBinaryandxs:base64Binaryare now mutually comparable, which means for example that the keysxs:hexBinary("")andxs:base64Binary("")can no longer co-exist in the same map: they are considered to be duplicates.