XPath 3.1 Maps and Arrays: Bridging XML and JSON
XPath 3.1 introduced map and array data
types to expand the XQuery and XPath Data Model (XDM) beyond traditional
hierarchical XML nodes. This article explains the syntax and behavior of
XPath 3.1 maps and arrays, demonstrates how they directly model JSON
structures, and highlights how these additions allow developers to
seamlessly query, transform, and integrate both XML and JSON within a
single processing pipeline.
The Need for Maps and Arrays in XPath 3.1
Prior to XPath 3.1, the data model relied entirely on sequences of atomic values and XML nodes (elements, attributes, text nodes). While sequences handled ordered lists of XML items well, they lacked two critical capabilities required for modern data processing:
- Nested collections: Standard XPath sequences cannot contain other sequences; any nested sequence automatically flattens.
- Key-value lookups: Associative lookups required searching XML attributes or elements using predicates, which was inefficient for arbitrary key-value mappings.
With the widespread adoption of JSON in web APIs and modern
architectures, XPath required native structures capable of modeling JSON
objects and JSON arrays without converting everything into synthetic XML
trees. The W3C resolved this in XPath 3.1 by adding map and
array item types as first-class citizens in the data
model.
The Array Data Type
An XPath 3.1 array is an ordered list of items where
each entry—termed a member—can be any sequence of zero or more
items. Unlike standard sequences, arrays do not flatten when nested
inside other arrays.
Syntax and Creation
Arrays can be created using the square-bracket constructor or the
array keyword:
(: Square-bracket constructor :)
[ "apple", "banana", "cherry" ]
(: Array containing nested sequences :)
[ (1, 2, 3), ("a", "b"), [] ]
(: Array constructor using a sequence :)
array { 1 to 5 }
Accessing Array Members
Array members are 1-indexed. Members can be retrieved using
functional invocation or the lookup operator (?):
let $fruits := [ "apple", "banana", "cherry" ]
return $fruits(1) (: Returns "apple" :)
(: Using the lookup operator :)
$fruits?2 (: Returns "banana" :)
(: Wildcard lookup to extract all members as a sequence :)
$fruits?* (: Returns ("apple", "banana", "cherry") :)
The Map Data Type
An XPath 3.1 map is a collection of key-value pairs.
Keys must be atomic values (such as strings, integers, or URIs), while
values can be any arbitrary sequence of items, including nodes, arrays,
or other maps.
Syntax and Creation
Maps are created using the map constructor:
map {
"name": "Jane Doe",
"id": 1042,
"active": true(),
"roles": [ "admin", "editor" ]
}
Accessing Map Entries
Values within a map can be retrieved using functional invocation, string key references, or the lookup operator:
let $user := map { "name": "Jane Doe", "id": 1042 }
return $user("name") (: Returns "Jane Doe" :)
(: Using the lookup operator :)
$user?id (: Returns 1042 :)
Bridging the Gap Between XML and JSON
The inclusion of map and array data types
creates a direct 1:1 conceptual mapping between JSON and XPath data
models:
- JSON Object maps to an XPath
map - JSON Array maps to an XPath
array - JSON Strings, Numbers, Booleans, and Null map to
their respective XDM atomic types (with
nullrepresented as an empty sequence orfn:json-null())
Native JSON Parsing and Serialization
XPath 3.1 introduces built-in functions that convert raw JSON strings directly into these native structures:
fn:parse-json($json-string): Parses a JSON string into an XDM map, array, or scalar value.fn:serialize($xdm-item, map { "method": "json" }): Serializes native maps and arrays directly back to a JSON string.
let $json := '{"status": "ok", "items": [10, 20, 30]}'
let $data := parse-json($json)
return $data?items?1 (: Returns 10 :)
Unified Multi-Format Processing
Because maps and arrays are native XDM items, they can hold XML
nodes, and XML nodes can reference or generate maps and arrays. In
environments supporting XPath 3.1 (such as XSLT 3.0 or XQuery 3.1
processors), a developer can read an XML document, query a REST JSON
endpoint using fn:json-doc(), combine the data structures
using standard path expressions, and output either XML or JSON without
intermediary third-party conversion libraries.