jsonpath icon indicating copy to clipboard operation
jsonpath copied to clipboard

Filtering Single Objects Based on Member Values

Open sflanker opened this issue 7 years ago • 0 comments

The original jsonpath "specification" seems a bit ambiguous on what @ should be bound to when a child script filter (i.e. [?(...)]) is applied to a member that contains a single object as opposed to an array (although, it is explicitly called out that it differs from xpath where filters are always applied to the current node set). The approach in this implementation is to enumerate the key value pairs in the object, and filter each one binding @ to the value (the key is disregarded). Unfortunately this makes certain filters impossible when dealing with singular objects rather than arrays. Given an object like:

{ "id": "123xyz",
  "type": { "code": "foo", "text": "Foo Document" } }

If I want to use jsonpath to return the document type object only if the code is foo, this is not possible. The expressions $.type[@ === "foo"] or $[@.code === "foo"] would both work in this simple example, but if either the type object contained another field with the value "foo" or the root object contained another member with a code attribute with the value "foo" jsonpath could false-positive on an object where $.type.code was not "foo".

One solution to this problem would be to change what @ gets bound to to be the whole object in these cases instead of enumerating the object keys and binding it to the respective values. It would still be possible to traverse all the children of an object using .* or [*]. However this is obviously not backward compatible, would presumably be a departure from the original jsonpath implementation, and would be difficult to implement here because the traversal logic for matching members by name is coupled with the logic for filtering.

An alternative solution is to introduce a name() function in the scope that filter expressions are evaluated in. This function would, much like its counterpart in xpath, return the name of the current node, which would be the index into the array or the key on an object depending on which was being enumerated. This has the advantage of being backward compatible and very easy to implement. Given this my query expression would be: $.type[name() === "code" && @ === "foo"].

sflanker avatar Oct 03 '17 23:10 sflanker