JSONの絞り込み
JSONPathのフィルター式で、必要な部分だけを取り出せます。
ドキュメント
結果
貼り付けたものがブラウザの外に出ることはありません。 connect-src の許可リストにより、これは約束ではなくブラウザによる保証になっています。 自分で確かめる
JSONPathのフィルター式を使って、大きなドキュメントを必要な部分だけに絞り込みます。結果は、一致した値のJSON配列としてそのままコピーできる形で返ります。
これが何であるかをはっきりさせておくと、これはJSONPathであってjqではありません。jqは完全な言語であり、複雑な変換には本当に優れた道具です。こちらは選択のためのもので、実際のところ「絞り込み」の大半は選択です。
フィルター式
毎回調べ直すことになる部分なので、構文をひとまとめにしておきます。
- $.items[[email protected]]
- キーが存在し、真である要素すべて。存在するかどうかだけでも妥当な条件です。
- $.items[[email protected] < 10]
- 数値の比較。大小比較は数値どうし、または文字列どうしにのみ適用されます。
- $.items[[email protected] == 'book']
- 文字列の比較。リテラルはクォートが必要で、ここが最もよくある間違いです。
- $.items[[email protected] < 10 && @.stock > 0]
- &&で論理積、||で論理和、括弧でグループ化します。
- $.items[[email protected]]
- 否定。ここではキーが存在しないか偽であることを意味します。
- $.items[?length(@.tags) > 2]
- 関数拡張。length()は文字列・配列・オブジェクトで使えます。
- $.items[?match(@.sku, '[A-Z]{3}-[0-9]+')]
- 値全体にアンカーされる正規表現。部分一致にはsearch()を使ってください。
- $..[[email protected] == 42]
- あらゆる深さに適用されるフィルター。どこにあるか分からないレコードを探すときの書き方です。
キーが存在しない場合
RFC以前の実装がもっとも食い違っていた部分なので、明示しておきます。RFC 9535では、何も選択しないクエリは「欠落」であり、欠落した値は別の欠落した値とだけ等しくなります。したがってpriceが存在しないとき@.price < 10は偽になります。エラーにもならず、一致もしません。==の比較が真になるには、両辺がともに欠落している必要があります。
実務上の帰結として、存在しないことを判定するには@.price == nullではなく[email protected]を使ってください。nullは値であり、欠落はそうではないからです。
How to do this in code
コードでの絞り込み。たいていはjqが正解になる場面です。
sh jq
3つめの例が判断の分かれ目です。グループ化や集計が必要なら、jqを使ってください。
# Select, then reshape
jq '[.items[] | select(.price < 10) | {sku, price}]' data.json
# Filter at any depth
jq '[.. | objects | select(.id? == 42)]' data.json
# Group and aggregate, which JSONPath cannot do at all
jq 'group_by(.category) | map({category: .[0].category, n: length})' data.json py Python
# A comprehension beats a query language for anything you
# can express directly.
cheap = [i for i in data['items'] if i['price'] < 10]
# JMESPath when the filter is configuration rather than code
import jmespath
cheap = jmespath.search("items[?price < `10`]", data) js JavaScript
const cheap = data.items.filter((i) => i.price < 10);
// Deep search without a library
function findAll(node, test, out = []) {
if (node && typeof node === 'object') {
if (test(node)) out.push(node);
for (const v of Object.values(node)) findAll(v, test, out);
}
return out;
}
const matches = findAll(data, (n) => n.id === 42); よくある質問
- なぜjqのプレイグラウンドにしないのですか?
- 本物のjqをブラウザで動かすには、WebAssemblyにコンパイルしたものを配信する必要があり、それはおよそ1メガバイトになります。読み込みの速さを売りにしているサイトで、多くの人が変換ではなく選択に使う機能のためにそれを払うのは割に合いません。また、JSONPathのフィルターを「jqのプレイグラウンド」と呼ぶのは嘘であり、このサイトはそういうことをしません。
- 絞り込みと同時に形を作り替えられますか?
- JSONPathではできません。ノードを選択するだけで、新しいノードを組み立てることはしません。マルチセレクトハッシュのあるJMESPathか、jqを使ってください。
- ドキュメント内のどこかにある、特定のキーを持つオブジェクトをすべて探すには?
- $..[?@.そのキー]で、あらゆる深さにフィルターが適用されます。特定のレコードを探すなら$..[[email protected] == 42]です。