ietf-wg-jsonpath/draft-ietf-jsonpath-base

Optional root (`$`) selector

Closed this issue · 12 comments

A number of implementations support implicit root selector (e.g. key instead of $.key).

Proposal

The standard should support optional root selector.

It should be supported since it is widely accepted and also a handy shortform (which I expect to be frequently used).

As an example, we should take into account that this behavior is supported by JavaScript jsonpath and jsonpath-plus that counts for a total of ~800000 weekly downloads and ~900 dependents on npm. Furthermore this is supported by Goessner implementation too.

For the sake of simplicity a path starting with $ should be defined as "canonical form" / "normal form" on the specification.

Therefore, the following:

"Syntactically, a JSONPath consists of a root selector ($), which selects the root node of a JSON document, followed by a possibly empty sequence of matchers."

might be rephrased as:

"Syntactically, a canonical JSONPath consists of a root selector ($), which selects the root node of a JSON document, followed by a possibly empty sequence of matchers."

As a consequence of this feature (for consistency reasons) ["key"] and [0] should be supported as well.

We also should take a decision about .key, which is supported by few popular implementations.

I disagree with this proposal. The $ is an identifier which represents the root of the JSON instance. It can also be used in query expressions (#17).

If individual implementations wish to add support for omitting this character, they can do so without being out of compliance with the spec.

I used to think this was a good idea, but have since changed my mind, and now share gregsdennis's view. As gregsdennis noted, there are natural extensions to the expression syntax that benefit from being able to base the expression at either the root ($) or the current (@) node, and generally, to my eye, the notation is more comprehensible with these symbols explicit.

glyn commented

Perhaps we should discuss:

If individual implementations wish to add support for omitting this character, they can do so without being out of compliance with the spec.

It depends. For example, if the spec requires an implementation to return an error if an invalid selector is provided, then implementations wouldn't be able to add this support without becoming non-compliant.

The alternative is to make the spec somehow more permissive. For example, we could say that if an invalid selector is provided, then an implementation MAY return an error. My concern with a more permissive approach would be the difficulty of testing that for compliance.

The alternative is to make the spec somehow more permissive.

This was my point in saying it.

we could say that if an invalid selector is provided, then an implementation MAY return an error.

Actually, you need to invert the inflection: say that the implementation SHOULD return an error. Leave it at that. This opens the door for optional support of additional syntaxes without explicitly stating it.

SHOULD is strong enough that an implementor should think twice before deciding against the requirement, but allows them to still claim compliance if they choose not to.

If you want to take it further, the spec could state that the default behavior of the implementation MUST be per spec, but it could have an option or switch that enables extended support.

My concern with a more permissive approach would be the difficulty of testing that for compliance.

JSON Schema is quite permissive and has a very extensive test suite. Test what the spec requires. Maybe have optional tests for common permissive cases, or let implementations independently test their optional support.

As an added note here, I've been looking into JMESPath, and it loos like they've opted to drop the "start path" $ from their syntax (it's not even optional, they've just omitted it). As a result, they can't do something like this:

$.foo[?(@.bar == $.baz)]
glyn commented

Interesting. Can they do something semantically equivalent by leaving out the $?

hmm ... so equivalent to

.foo[?(@.bar == .baz)]

would then be

['foo'][?(@.bar == ['baz'])]

That looks quite confusing, in my opinion. If I omit $ in $.foo, I consider root as "current context"; but if I write .foo within a filter - it seems like I had dropped @ as it's normally considered as "current context" there. And allowing to drop just $ but not @ looks inconsistent to me.

I would prefer to keep $/@ explicit. I don't see any significant profit from making them optional, except for BC with some implementations. I think that consistency of syntax is more important for the standard than BC.

As an added note here, I've been looking into JMESPath, and it loos like they've opted to drop the "start path" $ from their syntax (it's not even optional, they've just omitted it).

JMESPath as defined in JMESPath Specification doesn't have a symbol for the root value, there is no counterpart to '$', so it would be more accurate to say that they drop the '@.' from their syntax, where @ represents the current node (the current node being the same as the root at the start of evaluation.) In JMESPath, @.bar would be equivalent to just bar, and @[0] would be equivalent to just [0]. The current node symbol @ is, according to the specification, "only allowed when it's used in a bare expression", i.e. in contexts such as

@ == `[1,2]`

or

count(@)

(However, I think the grammar does allow @.bar in addition to bar).

As a result, they can't do something like this:

$.foo[?(@.bar == $.baz)]

There were proposals to introduce lexical scoping in JMESPath, see James James Saryerwinnie's original proposal and Michael Dowling's sugestion. These proposals would e.g. support binding the value of baz at the start of evaluation (equivalent to $.baz) to a variable named foo, and thus support something like bar == $foo, where the variable foo is referenced using $foo notation. These proposals never made it into the JMESPath specification, though.

Some implementations of JMESPath extend the syntax to support the $ symbol to refer to the root, and allow bar == $.baz.

See also #76, which compares the meaning of "current node" in JSONPath, JMESPath, and XPath 3.1.

hmm ... so equivalent to

.foo[?(@.bar == .baz)]

would then be

['foo'][?(@.bar == ['baz'])]

The equivalent JMESPath expression would be

foo[?bar == baz]

or

foo[?"bar" == "baz"]
cabo commented

Closed in #115

What was the resolution? Did we decide to require $ at the start of a path (and either $ or @ for expression paths)?