jqlang / jq

Command-line JSON processor
https://jqlang.github.io/jq/
Other
30.49k stars 1.58k forks source link

Whenever a key is not set and using ?, I would not expect all other concatenations to be losed #1095

Closed netei closed 8 years ago

netei commented 8 years ago

echo '[]' | jq 'if .key? == null then "" else "" end + "bar"'

Output: <nothing> Expected output : "bar"

I know my input is an array and not an object, and with an object, the following code works correctly.

However, I think it would make sense that .key? doesn't magically stops the execution of the whole line.

What do you think ?

pkoppstein commented 8 years ago

In response to the question @netei:

What do you think?

I'd say it makes sense to look at the manual, which has this to say about .foo?:

Just like .foo, but does not output even an error when . is not an array or an object.

It's worth understanding the important role that empty and backtracking play in jq. Meanwhile, to test whether the input is an object with a key, there are a couple of alternatives: the pedestrian way would be to test if it's an object (using type) and whether it has a key (using has). An alternative using ? would be based on [ .foo? ] because [empty] evaluates to []. Chances are, though, that you don't really need to perform such a test; that is, chances are that you can accomplish your actual task using backtracking.

ghost commented 8 years ago

As a workaround, you can use the alternative operator:

echo '[]' | jq 'if .key? // null == null then "" else "" end + "bar"'

As with your stated desired behaviour, this has the disadvantage of making it impossible to tell apart {"key": null} and {}. A better approach would be to collect .key? on a list and compare it to the empty list:

echo '[]' | jq 'if [.key?] == [] then "" else "" end + "bar"'

netei commented 8 years ago

And how would you delete a key only if it exists ?

I'm doing del(.msg,.requestId) but this won't work if the input is an array , for example :

echo '[]' | jq 'del(.msg,.requestId)'

fails with jq: error (at <stdin>:1): Cannot index array with string "msg"

Should I use del(.msg?,.requestId?) in that case ?

pkoppstein commented 8 years ago

And how would you delete a key only if it exists ?

del(.a?) will work as you suggest, but that's just a bit of jq magic. As far as the specification of intended behavior is concerned, it's important to distinguish the two questions:

(1) is the operation defined on a particular type? (2) does the object have the specified key?

Consider:

$ jq -nc '{"a":1,"b":2} | del(.a)'
{"b":2}
$ jq -nc '{"a":1,"b":2} | del(.c)'
{"a":1,"b": 2}
$ jq -n '[] | if type == "object" then del(.c) else . end'
[]
nicowilliams commented 8 years ago

@netei The ? operator basically catches any errors and executes empty instead.

If you want to make sure that an object has a value for some key, even if it is null, you have to use has().

There's an open issue, IIRC, to add a ! operator to make it easier to express that some key must have a value, and so on.