Arrays in XPath

XPath 3.1 introduces arrays as a new data type. (Unlike maps, arrays are not defined for use in XSLT 3.0 unless XPath 3.1 is supported. But Saxon's XSLT 3.0 implementation always supports XPath 3.1.)

The main reason arrays were introduced was to allow JSON data structures to be represented faithfully. However there are many other ways you can take advantage of arrays.

Arrays differ from sequences in a number of ways:

Arrays, like all other XDM values, are immutable. When you append or replace or remove a member entry in an array, you get a new array; the original is unchanged. From Saxon 9.9, the implementation uses a "persistent immutable" data structure under the covers, to ensure that making a small change to an array (such as replacing a single member) does not require copying the entire array.

As with sequences and maps, arrays do not have an intrinsic type of their own, but rather have a type that can be inferred from what they contain. An array conforms to the type array(T) if all of its members are of type T. For example if the members are all strings, then the array conforms to the type array(xs:string).

There are several ways to create an array:

There are no XSLT 3.0 instructions for creating arrays, analogous to the instructions xsl:map and xsl:map-entry. Saxon however fills the gap with the instructions saxon:array and saxon:array-member: see Extension instructions.

The summary of the full list of functions that operate on arrays is as follows; for full details see the Functions library. The prefix array represents the namespace URI http://www.w3.org/2005/xpath-functions/array.

Arrays: technical implementation details

Internally Saxon (from release 9.9) uses two implementations of arrays.

The first implementation, SimpleArrayItem, is backed by a Java ArrayList<Sequence>. An array constructor (either [x,y,z] or array{a,b,c}) will always deliver a SimpleArrayItem. This is economical on storage and has good performance for accessing specific items by index position, or for scanning the entire array. It is less well suited to incremental modification.

The other implementation, PersistentArrayItem, is backed by a persistent (immutable) list implemented as a tree structure. This makes it possible to add, replace, or remove entries in constant time without copying the entire array.

The result of operations such as array:put(), array:insert-before(), and array:remove() is always a PersistentArrayItem, even if the input is a SimpleArrayItem. Converting one to the other takes time proportional to the size of the array.

The result of array:filter(), array:for-each(), array:for-each-pair(), and array:reverse() is always a SimpleArrayItem, even if the input is a PersistentArrayItem. In cases where an array goes through a construction phase and is then used heavily for retrieval-only access, it might be worth forcing it back to a SimpleArrayItem at the end of the construction phase: this can be achieved by means of a call on array:filter() with a predicate that selects all members.