JSONの深い階層にある値を取るには

JSONの深い階層にある値を取ろうとすると、第一階層から順にパス式を記載しなければならない。アドホックな対応で長いパス式を書くのは面倒くさいので、いきなり深い階層のkeyを指定してvalueを取得する方法を書く。

以下のJSONを例にする

{"level1key":{"level2key":{"level3key":{"level4key":"level4value"}}}}

このJSONからlevel4keyのvalueを取得するには、 jqでlevel4keyにたどり着くまでのパス式を書く必要がある。

$ jq ".level1key.level2key.level3key.level4key" <<< '{"level1key":{"level2key":{"level3key":{"level4key":"level4value"}}}}'
"level4value"

..と?を組み合わせてアドホックに取得する

しかし..?と組み合わせるとlevel4keyだけでvalueを取得できる

$ jq "..|.level4key?" <<< '{"level1key":{"level2key":{"level3key":{"level4key":"level4value"}}}}'
null
null
null
"level4value"

..は要素内のすべての子要素を対象に探索する

..はmanによるとXPathの//と同じで要素内のすべての子要素を対象としてマッチをかけてくれる。

.. Short-hand for recurse without arguments. This is intended to resemble the XPath // operator. Note that ..a does not work; use ..|a instead. In the example below we use ..|.a? to find all the values of object keys "a" in any object found "below" ..

       jq '..|.a?'
          [[{"a":1}]]
       => 1

..|.level4keyと書けば第一階層以下すべての中でlevel4keyにマッチするものを取得してくれる。

?は要素が存在しないときにエラーを抑制する

level4keyが存在しない要素(第一階層自体、level1key内, level2key内では直接level4keyが存在しない)ではnullとともにjq: error (at <stdin>:1): Cannot index string with string "level4key"というエラーが出てしまうので、存在しなくてもエラーが出ないように?をつけている。

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

       jq '.foo?'
          {"foo": 42, "bar": "less interesting data"}
       => 42

       jq '.foo?'
          {"notfoo": true, "alsonotfoo": false}
       => null

nullを消して整形する

現状はnullの表示が邪魔なためselectを使ってnullを消す。

 $ jq "..|.level4key?|select(.!=null)" <<< '{"level1key":{"level2key":{"level3key":{"level4key":"level4value"}}}}'
"level4value"