1. 概要

JSON は、最近のほとんどのAPIおよびデータサービスで一般的に使用されている、広く使用されている構造化データ形式です。 軽量でJavaScriptとの互換性があるため、Webアプリケーションで特に人気があります。

残念ながら、BashなどのシェルはJSONを直接解釈して操作することはできません。これは、コマンドラインを介したJSONの操作が煩雑になる可能性があることを意味し、sedなどのツールの組み合わせを使用したテキスト操作が含まれます。 およびgrep

このチュートリアルでは、 JSON用の雄弁なコマンドラインプロセッサであるjqを使用して、この厄介さを軽減する方法を見ていきます。

2. インストール

まず、インストール jq から始めましょう。これは、ほとんどのオペレーティングシステムのパッケージリポジトリで利用可能です。 バイナリを直接ダウンロードしたり、ソースからビルドしたりすることもできます。

パッケージをインストールしたら、jqを実行してインストールを確認しましょう。

$ jq
jq - commandline JSON processor [version 1.6]

Usage:	jq [options] <jq filter> [file...]
	jq [options] --args <jq filter> [strings...]
	jq [options] --jsonargs <jq filter> [JSON_TEXTS...]
...

インストールが成功すると、バージョン、使用例、その他の情報がコンソールに表示されます。

3. 単純なフィルターの操作

jqは、JSONのストリームに対して機能するフィルターの概念に基づいて構築されています。各フィルターは入力を受け取り、JSONを標準出力に出力します。 これから説明するように、使用できる定義済みのフィルターは多数あります。 また、パイプを使用してこれらのフィルターを簡単に組み合わせて、複雑な操作や変換をすばやく構築してJSONデータに適用できます。

3.1. JSONをきれいにする

ちなみに、 jq の最も便利で頻繁に使用される機能の1つである、すべての中で最も単純なフィルターを見てみましょう。

echo '{"fruit":{"name":"apple","color":"green","price":1.20}}' | jq '.'

単純なJSON文字列をechoし、それをjqコマンドに直接パイプします。 次に、IDフィルター「。」を使用します。 これは入力を受け取り、それを出力として変更せずに生成しますが、デフォルトではjqがすべての出力をきれいに出力するという警告があります。

これにより、次の出力が得られます。

{
  "fruit": {
    "name": "apple",
    "color": "green",
    "price": 1.2
  }
}

このフィルターをJSONファイルに直接適用することもできます。

jq '.' fruit.json

JSONをプリティファイできることは、APIからデータを取得し、明確で読みやすい形式で応答を確認する場合に特に役立ちます。

curl を使用して単純なAPIをヒットし、実際にこれを確認してみましょう。

curl http://api.open-notify.org/iss-now.json | jq '.'

これにより、国際宇宙ステーションの現在の位置に対するJSON応答が得られます。

{
  "message": "success",
  "timestamp": 1572386230,
  "iss_position": {
    "longitude": "-35.4232",
    "latitude": "-51.3109"
  }
}

3.2. プロパティへのアクセス

別の単純なフィルターである.field演算子を使用してプロパティ値にアクセスできます。プロパティ値を見つけるには、このフィルターとそれに続くプロパティ名を組み合わせるだけです。

簡単な果物の例に基づいてこれを見てみましょう。

jq '.fruit' fruit.json

ここでは、このキーのすべての子を提供するフルーツプロパティにアクセスしています。

{
  "name": "apple",
  "color": "green",
  "price": 1.2
}

プロパティ値をチェーンして、ネストされたオブジェクトにアクセスできるようにすることもできます。

jq '.fruit.color' fruit.json

予想どおり、これは単に果物の色を返します。

"green"

複数のキーを取得する必要がある場合は、コンマを使用してそれらを区切ることができます。

jq '.fruit.color,.fruit.price' fruit.json

これにより、両方のプロパティ値を含む出力が生成されます。

"green"
1.2

プロパティの1つにスペースまたは特殊文字が含まれている場合、jqコマンドからアクセスするときに、プロパティ名を引用符で囲む必要があることに注意してください。

echo '{ "with space": "hello" }' | jq '."with space"'

4. JSON配列

次に、JSONデータの配列を操作する方法を見てみましょう。通常、配列を使用してアイテムのリストを表します。 また、多くのプログラミング言語と同様に、配列の開始と終了を示すために角かっこを使用します。

4.1. 反復

配列を反復処理する方法を示す基本的な例から始めます。

echo '["x","y","z"]' | jq '.[]'

ここでは、オブジェクト値イテレータ演算子。[] が使用されていることがわかります。これにより、配列内の各項目が別々の行に出力されます。

"x"
"y"
"z"

ここで、JSONドキュメントで果物のリストを表現したいとします。

[
  {
    "name": "apple",
    "color": "green",
    "price": 1.2
  },
  {
    "name": "banana",
    "color": "yellow",
    "price": 0.5
  },
  {
    "name": "kiwi",
    "color": "green",
    "price": 1.25
  }
]

配列内の各アイテムは、果物を表すオブジェクトです。

配列内の各オブジェクトから各果物の名前を抽出する方法を見てみましょう

jq '.[] | .name' fruits.json

まず、。[]を使用して配列を反復処理します。 次に、パイプ|を使用して、配列内の各オブジェクトをコマンドの次のフィルターに渡すことができます。 最後のステップは、.nameを使用して各オブジェクトから名前フィールドを出力することです。

"apple"
"banana"
"kiwi"

もう少し簡潔なバージョンを使用して、配列内の各オブジェクトのプロパティに直接アクセスすることもできます

jq '.[].name' fruits.json

4.2. インデックスによるアクセス

もちろん、すべての配列と同様に、インデックスを渡すことで、配列内のアイテムの1つに直接アクセスできます。

jq '.[1].price' fruits.json

4.3. スライス

最後に、jqは配列のスライスもサポートしています。これはもう1つの強力な機能です。これは、配列のサブ配列を返す必要がある場合に特に便利です。

繰り返しますが、単純な数値の配列を使用してこれを見てみましょう。

echo '[1,2,3,4,5,6,7,8,9,10]' | jq '.[6:9]'

結果は、インデックス6(包括的)からインデックス9(排他的)までの要素を含む長さ3の新しい配列になります。

[
  7,
  8,
  9
]

スライス機能を使用するときに、インデックスの1つを省略することもできます。

echo '[1,2,3,4,5,6,7,8,9,10]' | jq '.[:6]' | jq '.[-2:]'

。[:6] で2番目の引数のみを指定したため、スライスは配列の先頭から始まり、インデックス6まで実行されます。 。[0:6]を実行するのと同じです。

2番目のスライス操作には負の引数があります。これは、この場合、配列の最後から逆方向にカウントされることを示します。

2番目のスライスの微妙な違いに注意してください—最初の引数としてインデックスを渡します。 これは、末尾(-2)から2つのインデックスを開始し、2番目の引数が空であるため、配列の末尾まで実行されることを意味します。

これにより、次の出力が得られます。

[
  5,
  6
]

5. 関数の使用

jq には、さまざまな便利な操作を実行するために使用できる多くの強力な組み込み関数があります。 それらのいくつかを見てみましょう。

5.1. キーの取得

オブジェクトのキーを値ではなく配列として取得したい場合があります。

これは、キー関数を使用して実行できます。

jq '.fruit | keys' fruit.json

これにより、キーがアルファベット順にソートされます。

[
  "color",
  "name",
  "price"
]

5.2. 長さを返す

配列とオブジェクトのもう1つの便利な関数は、length関数です。

この関数を使用して、オブジェクトの配列の長さまたはプロパティの数を返すことができます

jq '.fruit | length' fruit.json

ここでは、フルーツオブジェクトに3つのプロパティがあるため、「3」を取得します。

文字列値にも長さ関数を使用できます

jq '.fruit.name | length' fruit.json

フルーツ名のプロパティには「apple」という5つの文字があるため、結果の出力として「5」が表示されます。

5.3. マッピング値

map関数は、フィルターまたは関数を配列に適用するために使用できる強力な関数です

jq 'map(has("name"))' fruits.json

この例では、配列内の各アイテムにhas関数を適用し、nameプロパティがあるかどうかを確認しています。単純なフルーツJSONでは、trueを取得します。各結果アイテム。

map 関数を使用して、配列内の要素に演算を適用することもできます。

各果物の価格を上げたいと想像してみましょう。

jq 'map(.price+2)' fruits.json

これにより、価格が上がるたびに新しい配列が得られます。

[
  3.2,
  2.5,
  3.25
]

5.4. 最小値と最大値

入力配列の最小要素または最大要素を見つける必要がある場合は、最小関数と最大関数を利用できます

jq '[.[].price] | min' fruits.json

同様に、JSONドキュメントで最も高価な果物を見つけることもできます。

jq '[.[].price] | max' fruits.json

これらの2つの例では、配列の反復の前後で [] を使用して、新しい配列を作成したことに注意してください。 これには、この新しいリストを最小または最大関数に渡す前の価格のみが含まれます。

5.5. 値の選択

select 関数は、JSONのクエリに使用できるもう1つの優れたユーティリティです。

これは、JSON用のXPathの単純なバージョンに少し似ていると考えることができます。

jq '.[] | select(.price>0.5)' fruits.json

これにより、価格が0.5を超えるすべての果物が選択されます。

同様に、プロパティの値に基づいて選択することもできます

jq '.[] | select(.color=="yellow")' fruits.json

条件を組み合わせて、複雑な選択を構築することもできます。

jq '.[] | select(.color=="yellow" and .price>=0.5)' fruits.json

これにより、特定の価格条件に一致するすべての黄色い果物が得られます。

{
  "name": "banana",
  "color": "yellow",
  "price": 0.5
}

5.6. 正規表現のサポート

次に、テスト関数を見ていきます。これにより、入力が特定の正規表現と一致するかどうかをテストできます

jq '.[] | select(.name|test("^a.")) | .price' fruits.json

簡単に言えば、名前が文字「a」で始まるすべての果物の価格を出力したいと思います。

5.7. 一意の値を見つける

一般的な使用例の1つは、配列内の特定の値の一意の出現を確認したり、重複を削除したりできるようにすることです。

FruitsJSONドキュメントにある固有の色の数を見てみましょう。

jq 'map(.color) | unique' fruits.json

map 関数を使用して、色のみを含む新しい配列を作成します。 次に、パイプを使用して、新しい配列の各色を一意の関数に渡します|。

これにより、2つの異なるフルーツカラーの配列が得られます。

[
  "green",
  "yellow"
]

5.8. JSONからのキーの削除

また、JSONオブジェクトからキーと対応する値を削除したい場合もあります。

このために、jqdel関数を提供します。

jq 'del(.fruit.name)' fruit.json

これにより、削除されたキーなしでフルーツオブジェクトが出力されます。

{
  "fruit": {
    "color": "green",
    "price": 1.2
  }
}

6. JSONの変換

JSONなどのデータ構造を操作する場合、あるデータ構造を別のデータ構造に変換したい場合がよくあります。 これは、複数のプロパティまたは値のみに関心がある場合に、大きなJSON構造を操作するときに役立ちます。

この例では、ページエントリのリストを説明するウィキペディアのJSONを使用します。

{
  "query": {
    "pages": [
      {
        "21721040": {
          "pageid": 21721040,
          "ns": 0,
          "title": "Stack Overflow",
          "extract": "Some interesting text about Stack Overflow"
        }
      },
      {
        "21721041": {
          "pageid": 21721041,
          "ns": 0,
          "title": "Baeldung",
          "extract": "A great place to learn about Java"
        }
      }
    ]
  }
}

各ページエントリのタイトルと抜粋にのみ本当に関心があります。

それでは、このドキュメントをどのように変換できるか見てみましょう。

jq '.query.pages | [.[] | map(.) | .[] | {page_title: .title, page_description: .extract}]' wikipedia.json

コマンドを正しく理解するために、コマンドをさらに詳しく見ていきます。

  • まず、pages配列にアクセスし、その配列をパイプを介してコマンドの次のフィルターに渡すことから始めます。
  • 次に、この配列を繰り返し処理し、pages配列内の各オブジェクトを map 関数に渡します。ここで、各オブジェクトのコンテンツを使用して新しい配列を作成するだけです。
  • 次に、この配列を繰り返し処理し、アイテムごとに2つのキーpage_titlepage_descriptionを含むオブジェクトを作成します。
  • .titleおよび.extract参照は、2つの新しいキーを設定するために使用されます。

これにより、新しい無駄のないJSON構造が得られます。

[
  {
    "page_title": "Stack Overflow",
    "page_description": "Some interesting text about Stack Overflow"
  },
  {
    "page_title": "Baeldung",
    "page_description": "A great place to learn about Java"
  }
]

7. 結論

この詳細な記事では、jqがコマンドラインを介してJSONを処理および操作するために提供する基本的な機能のいくつかについて説明しました。

最初に、 jq が提供するいくつかの重要なフィルターと、それらをより複雑な操作の構成要素として使用する方法について説明しました。 次に、jqにバンドルされている多数の組み込み関数の使用方法を確認しました。

最後に、あるJSONドキュメントを別のJSONドキュメントに変換する方法を示す複雑な例を示しました。

もちろん、より興味深い例については優れたクックブックを確認してください。いつものように、記事の完全なソースコードはGitHub入手できます。