JNodes
JNodes (think JSON Nodes) encapsulate the values found in a tree of maps and arrays, such as the tree formed by parsing a JSON text. JNodes can be used in path expressions in the same way as XML nodes (now called XNodes when a distinction is needed) are used in a tree formed by parsing XML.
Many operations apply both to JNodes and XNodes, so the term GNode is introduced to refer to both kinds generically.
Consider the tree formed by applying the parse-json function to a
(recursive) JSON text as follows:
The value of $tree is a map, and this can be wrapped in a JNode either explicitly
by a call on the function jtree($tree), or implicitly by using $tree
as the left hand operand of the / operator.
As a simple example, $tree/type (which is short for $tree/child::type)
returns a JNode that wraps the top-level map entry "type":"package". The important
properties of this JNode are its key ("type") and its value ("package"):
these properties are accessible using the functions jkey and jvalue.
It is also possible to navigate from a JNode to its descendants: the expression $tree//name
returns six JNodes, whose value properties are respectively "org",
"xml", "sax", "Attributes", "ContentHandler",
and "XMLReader". The path expression $tree//name[../type="class"]/jvalue()
returns the sequence ("Attributes", "ContentHandler", "XMLReader"). This illustrates that
it is possible to navigate from a JNode to its parent using the parent axis, here abbreviated in the usual
way to ... The path expression can be written in full as:
All the axes available for XNodes are also available for JNodes (including four new axes introduced in
XPath 4.0: following-or-self, preceding-or-self, following-sibling-or-self,
and preceding-sibling-or-self). This works only because maps in 4.0 are ordered: in the above
example, the preceding-sibling axis starting at a JNode whose key is "name"
is in each case a JNode whose key is "type".
When selecting the children of a JNode that wraps an array, the child JNodes again have a key
and a value property: this time, the key property is an integer giving the one-based
index of an array member, and the value property is the value of the array member. So the expression
$tree//name[.="ContentHandler"]/../jkey() returns 2, because the map whose name
entry is "ContentHandler" is the second member of the containing array.
The node tests used to select chosen nodes on an axis differ slightly from those used with XNodes. The most common node tests are:
*selects all the entries in a map, or all the members of an array.nameselects the entry in a map whose key is the stringname, which must take the form of an NCName.get("key value")selects the entry in a map whose key is"key value". In this case the key can be any atomic item (including, as in this example, a string containing spaces). When selecting members of an array,get(3)selects the third member.~typeselects JNodes whosevalueis of a given type. For examplechild::~xs:integerselects child JNodes whose value is an integer, whilechild::~array()selects child JNodes whose value is an array. Using record types is often convenient: in the above example,$tree//~record(type, name)selects all descendant maps having entries with keys"type"and"name".
JNodes are intended primarily for use with trees of maps and arrays derived from JSON, in which the values of entries/members are typically single items. They can also be used with maps and arrays whose values are multi-item sequences, but their use in such cases is slightly less convenient. For details consult the XPath 4.0 specification. It's entirely possible that the specification in this area will change over time.