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

Development of a JSONPath internet draft
https://ietf-wg-jsonpath.github.io/draft-ietf-jsonpath-base/
Other
58 stars 20 forks source link

Singular path as selector #522

Open gregsdennis opened 1 month ago

gregsdennis commented 1 month ago

Inspired by a StackOverflow question, I think a singular path could be a viable selector.

The example given is

addresses[productData.products[0].addressRefId].storeName

I see this as taking the value in productData.products[0].addressRefId as the selector value.

This would, for example, allow the data to contain the index of an array (or the key of an object) to select, amongst other similar functionality.

If we could work out a way to support multi-value paths, that'd be cool, but I think singular paths are simple enough to do.

He-Pin commented 1 month ago

But it can be expressed with the filter with test-expr.

vishalJ16 commented 1 month ago

@He-Pin Can you please help with the expression. I tried multiple ways drawing inspiration from here https://github.com/ietf-wg-jsonpath/draft-ietf-jsonpath-base/issues/156 but could not come up with a working one, because none of the method could give me the value of inner path productData.products[0].addressRefId , to be used as a "key" for outer path addresses["inner path evaluated value"].storeName

jg-rp commented 1 month ago

I like this syntax.

Equivalent results could be achieved using a filter selector and a non-standard key/index identifier or function. I've tried to describe such a current key identifier here (# is the name or index associated with @).

$.addresses[?# = $.productData.products[0].addressRefId].storeName

But a singular path being coerced to a name or index selector looks nicer to me.

He-Pin commented 1 month ago

@vishalJ16 I think the root cause is current filter selector only works on the member's value, We are facing the same problem ,and we are using [/$regexExp/$flags] to match the keys (only works on a JSON object)`

I would like to have a key filter, something like the @ but point to the member's name or idx.

Updated: I think @jg-rp 's idea is great:

  1. @ refs to the member value
  2. # refs to member name / index

Another thing we need to address is , how to pass the # to the current function definition, maybe we need add something to the type system.

vishalJ16 commented 1 month ago

@He-Pin What essentially you meant is @jg-rp expression is something which could have worked, but currently it's not possible, am I correct? And is it right to conclude that my requirement is not possible with the current version of jayway-jsonpath and It requires a code change?

He-Pin commented 1 month ago

@He-Pin What essentially you meant is @jg-rp expression is something which could have worked, but currently it's not possible, am I correct? And is it right to conclude that my requirement is not possible with the current version of jayway-jsonpath and It requires a code change?

With jayway, it's possibly, you need to register a pathfunction with reflection.

jg-rp commented 1 month ago

how to pass the # to the current function definition, maybe we need add something to the type system.

@He-Pin I've been treating the result of # as a ValueType, so standard functions that accept a ValueType can be used without modification.

$[?match(#, 'ab.*')]

An alternative might be to allow function extensions to accept a union of types for a parameter. Then, for example, match() could be defined to take an argument of ValueType | KeyType for its first parameter, and ValueType for the second.

jg-rp commented 1 month ago

Here's my attempt at describing a singular query selector.

I'm still undecided as to whether the root identifier should be explicit or implicit for embedded queries. If the embedded query starts with a bracketed selection, the $ might make it easier to read.

$.a.j[$['c d'].x.y]

vs

$.a.j[['c d'].x.y]

Singular Query Selector

The singular query selector consist of an embedded absolute singular query, the result of which is used as an object member name or array element index.

If the embedded query resolves to a string or int value, at most one object member value or array element value is selected. Otherwise the singular query selector selects nothing.

Syntax

selector                = name-selector /
                          wildcard-selector /
                          slice-selector /
                          index-selector /
                          filter-selector /
                          singular-query-selector

singular-query-selector = abs-singular-query

Examples

{
  "a": {
    "j": [1, 2, 3],
    "p": {
      "q": [4, 5, 6]
    }
  },
  "b": ["j", "p", "q"],
  "c d": {
    "x": {
      "y": 1
    }
  }
}
Query Result Result Path Comment
$.a[$.b[1]] {"q": [4, 5, 6]} $['a']['p'] Object name from embedded singular query
$.a.j[$['c d'].x.y] 2 $['a']['j'][1] Array index from embedded singular query
$.a[$.b] Embedded singular query does not resolve to a string or int value
vishalJ16 commented 1 month ago

With jayway, it's possibly, you need to register a pathfunction with reflection

Sorry I am a bit confused. To register my custom function to jsonpath, I would required these code changes https://github.com/json-path/JsonPath/pull/286 mentioned here https://github.com/json-path/JsonPath/issues/246, or can it be done without them? These changes still not merged.

He-Pin commented 1 month ago

With jayway, it's possibly, you need to register a pathfunction with reflection

Sorry I am a bit confused. To register my custom function to jsonpath, I would required these code changes https://github.com/json-path/JsonPath/pull/286 mentioned here https://github.com/json-path/JsonPath/issues/246, or can it be done without them? These changes still not merged.

You can change it with Java Reflection or MrthodHandles。

f3ath commented 1 month ago

FWIW, in my implementation I allow custom functions to be provided to the parser, with some available almost out-of-the-box:

    {
      "name": "key(), palindromic keys",
      "selector" : "$[?key(@) == reverse(key(@))]",
      "document" : {"foo": "FOO", "bar": "BAR", "bab":  "BAB", "": "", "a":  "A"},
      "result": ["BAB","","A"]
    },
gregsdennis commented 1 month ago

@f3ath, that doesn't pertain to this issue. This issue is about creating a new selector that is itself a path, not custom functions.

gregsdennis commented 1 month ago

@jg-rp, we have another issue for grabbing the key in an expression. Arguably, that allows for more functionality, but unfortunately it hasn't been received well.

gregsdennis commented 1 month ago

@jg-rp you bring up a good point about including the root selector. I think it should be required.

I wonder if the relative root makes sense to also define.

$..[@.a]

Recursively select the value in objects indicated by the object's a property.

Of course this entire feature can also only be defined for keys which are strings (names) or numbers (indices). It wouldn't select anything if the value ends up being any other JSON value type.