Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can I add a default value without a key? #295

Open
DMLfor opened this issue Sep 14, 2022 · 10 comments
Open

Can I add a default value without a key? #295

DMLfor opened this issue Sep 14, 2022 · 10 comments

Comments

@DMLfor
Copy link

DMLfor commented Sep 14, 2022

{
    "name": {
        "first": "Tom",
        "last": "Anderson"
    },
    "age": 37,
    "children": [
        "Sara",
        "Alex",
        "Jack"
    ],
    "fav.movie": "Deer Hunter",
    "friends": [
        {
            "first": "Dale",
            "last": "Murphy",
            "age": 44,
            "nets": [
                "ig",
                "fb",
                "tw"
            ],
            "others": [
                {
                    "k": "v",
                    "v": "k"
                },
                {
                    "k": "v",
                    "v": "k"
                }
            ]
        },
        {
            "last": "Craig",
            "age": 68,
            "nets": [
                "fb",
                "tw"
            ],
            "others": [
                {
                    "v": "k"
                },
                {
                    "k": "v",
                    "v": "k"
                }
            ]
        },
        {
            "first": "Jane",
            "last": "Murphy",
            "age": 47,
            "nets": [
                "ig",
                "tw"
            ],
            "others": [
                {
                    "k": "v",
                    "v": "k"
                },
                {
                    "v": "k"
                }
            ]
        }
    ]
}

Current results:

"friends.#.first"    >> ["Dale", "Jane"]
"friends.#.others.#.k"  >> [["v", "v"], ["v"],["v"]]

With default value:

"friends.#.first"    >> ["Dale", nil, "Jane"]
"friends.#.others.#.k"  >> [["v", "v"], [nil, "v"],["v", nil]]

Thanks:)

@ngarg-kr
Copy link

ngarg-kr commented Mar 2, 2023

@tidwall ^^

@volans-
Copy link

volans- commented Mar 7, 2023

@ngarg-kr I played a bit with multipaths, literals and modifiers and I think I got basically the same behaviour you were looking for.

Query:

friends.#.others.#.[{"k":!null},{k}].@join.@values|#.@flatten

Result:

[["v","v"],[null,"v"],["v",null]]

Explanation:

The [{"k":!null},{k}] part creates a multipaths array with the first item being {"k":null} and the second item being the item with key "k" coming from the others array. That is repeated for each item in the others array and if there is no key "k" an empty object {} is returned instead.
So the first part of the query friends.#.others.#.[{"k":!null},{k}] returns:

[[[{"k":null},{"k":"v"}],[{"k":null},{"k":"v"}]],[[{"k":null},{}],[{"k":null},{"k":"v"}]],[[{"k":null},{"k":"v"}],[{"k":null},{}]]]

Then I apply the @join modifier to merge the objects where the last wins, so where there is an empty object we keep the default literal value while where there is a value that one wins. See the dot-vs-pipe section of the docs for more details on the | separator.
At that point we get only the values discarding the keys with the @values modifier and finally apply the @flatten modifier to get rid of the additional nested layer of arrays.

Caveats:

I'm not totally sure all the steps are working as expected, I got there with some trial and error, and some modifier got applied to the inner layer a little bit automagically, hence I'm not sure that's the intended behaviour or possibly some bug that could potentially change behaviour in the future.

Other alternatives

Other possible alternatives that are simpler and would just need some additional parsing on your code are:

  1. query: friends.#.others.#.[k]
    result:
[[["v"],["v"]],[[],["v"]],[["v"],[]]]
  1. query: friends.#.others.#.{k}
    result:
[[{"k":"v"},{"k":"v"}],[{},{"k":"v"}],[{"k":"v"},{}]]

I also noticed that also using the @values modifier gives the same result of (1) but I'm not sure why or if that's expected: friends.#.others.#.k.@values

@litao09h
Copy link

litao09h commented Mar 21, 2023

@tidwall @volans-
The Same problem, eg:

arr  := gjson.GetBytes(jsonByte, `#(query%"*abc*")#.[resp.vid,resp.uid,resp.text,resp.errorno]`).Array()

Sometimes the key vid\uid may not exist, the length(arr[i]) is not 4
The expected result is a fixed array length of 4, and non-existent keys return null or empty

@litao09h
Copy link

@tidwall @volans- The Same problem, eg:

arr  := gjson.GetBytes(jsonByte, `#(query%"*abc*")#.[resp.vid,resp.uid,resp.text,resp.errorno]`).Array()

Sometimes the key vid\uid may not exist, the length(arr[i]) is not 4 The expected result is a fixed array length of 4, and non-existent keys return null or empty

If there is no way to return the default value, I can only append a string to complete the array length

@volans-
Copy link

volans- commented Mar 24, 2023

@tidwall @volans- The Same problem, eg:

arr  := gjson.GetBytes(jsonByte, `#(query%"*abc*")#.[resp.vid,resp.uid,resp.text,resp.errorno]`).Array()

Sometimes the key vid\uid may not exist, the length(arr[i]) is not 4 The expected result is a fixed array length of 4, and non-existent keys return null or empty

@litao09h could you provide the JSON input for your query please?

@litao09h
Copy link

@tidwall @volans- The Same problem, eg:

arr  := gjson.GetBytes(jsonByte, `#(query%"*abc*")#.[resp.vid,resp.uid,resp.text,resp.errorno]`).Array()

Sometimes the key vid\uid may not exist, the length(arr[i]) is not 4 The expected result is a fixed array length of 4, and non-existent keys return null or empty

@litao09h could you provide the JSON input for your query please?

We converted the log to json

a=11&b=12&c=13&d=14
a=21&b=22&d=24
a=31&c=33&d=34
b=41&c=43&d=44

After log conversion json

[{"a":11,"b":12,"c":13,"d":14},{"a":21,"b":22,"d":14},{"a":31,"c":33,"d":34},{"b":41,"c":43,"d":44}]

@volans-
Copy link

volans- commented Mar 25, 2023

@litao09h Sorry but I don't understand. I was asking what is the JSON to which you apply the query #(query%"*abc*")#.[resp.vid,resp.uid,resp.text,resp.errorno]. The one above doesn't seem to match at all your query.

@tidwall
Copy link
Owner

tidwall commented Mar 25, 2023

A default value can be ensured by using the [key,!"default"].0 pattern.

This uses a multipath array to put the key value into the first position of an array, and a literal value !"default" as the second, then uses a .0 path to select the first item in the array. Which will either be key if it exists, or !"default" if key does not exist.

To get the results @DMLfor is looking for in their original question.

friends.#.[first,!null].0        >> ["Dale",null,"Jane"]
friends.#.others.#.[k,!null].0   >> [["v","v"],[null,"v"],["v",null]]

@tidwall
Copy link
Owner

tidwall commented Mar 25, 2023

friends.#.others.#.[k,!null].0

is basically the same as what @volans- used in his example above

friends.#.others.#.[{"k":!null},{k}].@join.@values|#.@flatten

but it may be is a little easier to disect.

@litao09h
Copy link

A default value can be ensured by using the [key,!"default"].0 pattern.

This uses a multipath array to put the key value into the first position of an array, and a literal value !"default" as the second, then uses a .0 path to select the first item in the array. Which will either be key if it exists, or !"default" if key does not exist.

To get the results @DMLfor is looking for in their original question.

friends.#.[first,!null].0        >> ["Dale",null,"Jane"]
friends.#.others.#.[k,!null].0   >> [["v","v"],[null,"v"],["v",null]]

That works for me ! Thank you for your patient answer

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants