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:

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:

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:

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.

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 map can be omitted. So the expression {"x":1, "y":2} has the same effect as map{"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 let expression. For example, let {$x, $y} := $map return ... is equivalent to let $x := $map?x, $y := $map?y return ....

  • A variant of the XPath for expression (or in XQuery, the FLWOR expression) is introduced to allow iterative processing of the entries in a map, in order: for example string-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:hexBinary and xs:base64Binary are now mutually comparable, which means for example that the keys xs:hexBinary("") and xs:base64Binary("") can no longer co-exist in the same map: they are considered to be duplicates.