Use jq to get a value from two possible paths

jqjson

I wanted to use jq to get a specific value b from two possible paths (and these two only). So, basically, I wanted to say

jq '(path1 OR path2) | .b'

which should work if b is at one of the two paths path1 or path2. If both paths has b, then the first path takes precedence.

For example, both

echo '{"b":2}' | jq '.b'
echo '{"a": {"b":2}}' | jq '.a.b'

extract b at . and .a.

Can I somehow say

echo ... | jq '(MAGIC).b'

to make it work for both inputs above?

What I tried so far is to use the recursive operator ..:

jq '[..|.b?|values]|first'

This kind of works in that both:

echo '{"b":2}' | jq '[..|.b?|values]|first'
echo '{"a": {"b":2}}' | jq '..|.b?|values|first'

give 2. But it is not specific enough and also allows b to be anywhere in the tree below .. It is also difficult to read. In addition, it may not work if the two paths are not parent-children related.

(This is with jq 1.7 in Ubuntu 24.04 LTS)

Best Answer

.b // .a.b

This would extract the value of the top-level b key, but if that value is missing, null, or false (i.e, if the value is false in a boolean context), use the b key under a.

The above would be short and snappy if you knew that your top-level b key never corresponds to a boolean value. For a more general approach, you would need to test for existence of the b key rather than testing its value:

if has("b") then .b else .a.b end

This would extract the value of the top-level b key, if it exists, and would otherwise return the other key's value. This would be able to return false or null for the top-level b key if that is the value the key has.

As for your "magic" expression, in the exact form that you asked for it, i.e. (MAGIC).b:

(if has("b") then . else .a end).b

Here, the if statement returns either . or .a depending on whether the b key exists in the current object or not. The b key's value is the extracted from whichever object the "magic" expression returns.

Related Question