Using Recursive Decent In JQ

Yesterday, I discovered that JQ has a recursive descent operator.. — which allows one to go through each field of a JSON structure. When used, it will emit every value of a JSON structure, doing so in pre-order (i.e. any nested objects or arrays will be written to the output before they’re visited).

$ cat eg.json
{
  "name": "hello",
  "age": 123,
  "phone": {"home": 1, "mobile": 2}
}
$ jq '..' eg.json
{ "name": "hello", "age": 123, "phone": {"home": 1, "mobile": 2}}
"hello"
123
{"home": 1, "mobile": 2}
1
2

This allows for some useful operations, such as finding the value of a field without knowing the exact path to it, similar to the // operator in XPaths:

$ jq '.. | .mobile? | select(. != null)' eg.json
2

The use of select is necessary, as leaving it out will result in a whole lot of nulls being emitted, since every other node is not an object that has a mobile field.

One other thing that can be done is wrapping the entire expression in the path function to recreate the path to the node:

$ jq 'path(.. | .mobile? | select(. != null))' eg.json
["phone", "home"]

Reverse the string and join the elements with dots and you should be able to recreated the JQ paths you need to fetch the nested fields, saving you the need to use the recursive descent operator:

$ jq -r '"." + (
  path(..|.mobile?|select(. != null)) | reverse | join(".")
)' eg.json
.home.phone

Useful stuff.

Shell