The meaning of current value expressions in filters
There's a great deal of data, an almost overwhelming amount of data, in the JSONPath comparisons. The purpose of this document is to analyze that data as it pertains to the different implementations. It limits itself to comparing and contrasting the data, and attempting to understand it given some understanding of implementation internals. It is not prescriptive. It is intended as a collaborative effort that anyone can contribute to.
There are a great number of issues to cover, this is only the beginning.
Should JSONPath filters apply to both JSON objects and arrays, or only to JSON arrays? If to JSON objects, should they apply to the object itself, or to the value part of the object's name-value pairs?
In the comparisons, given document
{"key": 42, "another": {"key": 1}}
and selector
$[?(@.key)]
results vary.
Result | Interpretation | Count | Notable implementations |
---|---|---|---|
[], "syntax error", "not supported", or runtime error | Unsupported | 17 | Json.NET, PHP Goessner |
[ { "key": 1}] | Applied to value parts of members | 15 | Python jsonpath |
[ { "another": { "key":1}, "key": 42 }] | Applied to the object, returns the object if any object key matches | 10 | Java Jayway |
[ 42, { "key": 1 }] | Applied to object members and value parts of members | 2 | JavaScript Goessner |
Filter expression with bracket notation
Filter expression with bracket notation with number
Filter expression with subfilter
Current with dot notation
A current value expression is an expression that refers to the "current value", represented by @
.
Many (but not all) JSONPath implementations support selectors like
$[?(@['key']==42)]
$[?(@[1]=='b')]
Perhaps surprisingly, out of 44 implementations, 15 cannot evaluate the first selector,
and 13 the second, variously producing []
, "syntax error", "not supported", or runtime error.
But of the ones that can, they all agree on the same result.
What do expressions like @['key']
and @[1]
mean? On the one hand, they bear a resemblance
to JSONPath expressions, except they begin with @
rather than $
. On the other hand,
they must evaluate to a single JSON value (null, boolean, number, string, object, array),
not a result list, for the above comparisons to make sense.
In original JavaScript Goessner, Stefan Goessner uses the JavaScript engine for evaluating
JSONPath filter expressions. In these scripts, @
is substituted with the "current value",
and expressions consisting of the "current value" followed by a dot or a left square bracket
are evaluated according to JavaScript semantics. These always result in a single value.
Similar observations apply to implementations that use Python or PhP for evaluating filter expressions.
Following original JavaScript Goessner, some JSONPath implementations made up their own expression languages, and dispensed with JavaScript, Python, PhP et all for that purpose. Of these, the most popular and influential was Java Jayway.
In Jayway, expressions like @['key']
and @[1]
mean actual JSONPath expressions,
evaluated against the "current value". Moreover, Jayway allows JSONPath expressions
to start with either $
or @
at the beginning of evaluation, where $
and @
both represent the root value. Also in Jayway,
by default JSONPath expressions are evaluated according to rules that produce a single value,
not a result list. Some description of these rules may be found
here,
under "Usage notes and considerations". In summary, the slice, union and filter selectors
always wrap matches in an array, the wildcard selector wraps matches in an array if
there are more than two, the identifier and index selectors
produce a single value if matched, and if not matched, produce a null.
Jayway has an option to return a result list rather than a single value, but this option does not apply inside filters, inside filters, current value expression are always evaluated to a single value according to the above rules.
We can get an idea of the number of implementations that treat current value expressions as actual JSONPath expressions from the comparison that allows subfilters in filters.
Given the document
[
{
"a": [{"price": 1}, {"price": 3}]
},
{
"a": [{"price": 11}]
},
{
"a": [{"price": 8}, {"price": 12}, {"price": 3}]
},
{
"a": []
}
]
and selector
$[?(@.a[?(@.price>10)])]
15 of 44 implementations produce meaningful results, but with some differences. These include the important Java Jayway and Json.NET implementations.
The majority, 9 of 15, including Json.Net, produce
[
{
"a": [
{"price": 11}
]
},
{
"a": [
{"price": 8},
{"price": 12},
{"price": 3}
]
}
]
The remaining five, including Jayway, return the original document.
About half of the implementations that support subfilters, 8 of 15,
allow JSONPath expressions to start with either $
or @
at the beginning of evaluation,
where $
and @
both represent the root value.