0Pricing
DevOps Bootcamp · レッスン

jqによるJSONオブジェクトの変換と構築

map、to_entries、オブジェクト構築を使ってデータを整形し、新しいJSONペイロードを生成します。

「jqによるJSONオブジェクトの変換と構築」はCoddyKit上の無料DevOps Bootcampレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはDevOps Bootcamp学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 DevOps Bootcampコースには全4レッスンが含まれています。

JSON を変換する理由

API やログファイルから得られる生の JSON が、そのまま必要な形になっていることはほとんどありません。大きなオブジェクトから特定のフィールドだけを取り出したい場合や、キーの名前を変更したり、ネストされた構造をフラット化したり、別のサービスに送信するまったく新しいペイロードを作成したりする場合があります。

jq は軽量で強力なコマンドライン JSON プロセッサーです。1 つのパイプラインで、こうした変換を実行できます。このレッスンでは、データの形を組み替えるための 3 つの基本テクニックを学びます。

  • オブジェクトの構築 — 新しい JSON オブジェクトを一から作成します
  • map — 配列のすべての要素に変換を適用します
  • to_entries / from_entries — オブジェクトのキーと値の組を配列として扱い、フィルタリングして再構築できるようにします

すべての例では、jq がインストールされていることを前提とします(apt install jq / brew install jq)。

オブジェクト構築の基本

jq の最も基本的な機能はオブジェクトの構築です。式を {} で囲むことで、新しい JSON オブジェクトを作成します。含めるフィールドと、そのフィールドの名前を指定できます。

構文:

  • { newKey: .existingField } — フィールド名を変更します
  • { name, age } — 新しいキーがフィールド名と同じ場合の省略記法です
  • { total: (.price * .qty) } — 式の中で値を計算します

次のスニペットは商品 JSON を読み込み、計算した subtotal フィールドを含む、より簡潔な構造を生成します。

#!/usr/bin/env bash
# Object construction: pick and rename fields
product='{
  "id": 42,
  "name": "Widget Pro",
  "price": 9.99,
  "qty": 3,
  "warehouse": "EU-West"
}'

echo "$product" | jq '{
  productId: .id,
  name,
  subtotal: (.price * .qty)
}'

ネストされたデータからのオブジェクト構築

実際の JSON は、多くの場合ネストされています。jq では、オブジェクトコンストラクター内でネストされたパスをたどり、同時に構造をフラット化できます。

コンストラクターの値の式では、ドットパス記法を使用します。

  • { city: .address.city }
  • { lat: .location.coords.lat }

次の例では、深くネストされたユーザーレコードを、CSV のヘッダー行や API リクエストボディに適したフラットな概要へ変換します。

#!/usr/bin/env bash
user='{
  "id": "u-001",
  "profile": {
    "displayName": "Ada Lovelace",
    "contact": { "email": "ada@example.com", "phone": "+44-700" }
  },
  "plan": "pro"
}'

echo "$user" | jq '{
  id,
  name: .profile.displayName,
  email: .profile.contact.email,
  plan
}'

map による配列の変換

map(expr) は jq における for-each に相当します。入力配列のすべての要素に expr を適用し、同じ長さの新しい配列を返します。

重要なポイント:

  • map(expr) は [.[] | expr] の糖衣構文です
  • 内部の式には、オブジェクト構築を含む任意の jq フィルターを指定できます
  • select() と組み合わせて、変換前にフィルタリングできます

次のスニペットは注文の一覧を処理し、出荷明細に必要なフィールドだけを残します。

#!/usr/bin/env bash
orders='[
  {"orderId": 1, "customer": "Alice", "total": 42.50, "status": "shipped"},
  {"orderId": 2, "customer": "Bob",   "total": 18.00, "status": "pending"},
  {"orderId": 3, "customer": "Carol", "total": 99.99, "status": "shipped"}
]'

# Produce a shipping manifest: only shipped orders, slim fields
echo "$orders" | jq '[
  .[] | select(.status == "shipped") | {
    id: .orderId,
    recipient: .customer,
    amount: .total
  }
]'

計算フィールドを使った map

map の内部では、値をコピーするだけでなく、新しい値の計算、型の変換、フィールドの結合も行えます。よく使われるパターンは次のとおりです。

  • 文字列補間:"\(.first) \(.last)"
  • 算術演算:(.price * 1.2 | round) で 20% のマークアップを適用します
  • 条件分岐:if .score >= 90 then "A" else "B" end

次の例では、従業員の一覧に計算した fullName と、経験年数に基づく seniority ラベルを追加して情報を拡張します。

#!/usr/bin/env bash
staff='[
  {"first": "Grace", "last": "Hopper",  "years": 15},
  {"first": "Alan",  "last": "Turing",  "years": 4},
  {"first": "Linus", "last": "Torvalds","years": 9}
]'

echo "$staff" | jq 'map({
  fullName: "\(.first) \(.last)",
  years,
  seniority: (if .years >= 10 then "senior" elif .years >= 5 then "mid" else "junior" end)
})'

to_entries の理解

to_entries は JSON オブジェクトを {key, value} の組の配列に変換します。これにより、オブジェクトのフィールドに対して配列操作(map、select、sort)を実行できるようになります。オブジェクトに対しては、この操作を直接行えません。

変換例:

  • 入力:{"a": 1, "b": 2}
  • 出力:[{"key": "a", "value": 1}, {"key": "b", "value": 2}]

逆の操作は from_entries で、この配列をオブジェクトに戻します。これらを組み合わせた to_entries | map(...) | from_entries は、オブジェクト単位の変換で使われる慣用パターンです。

#!/usr/bin/env bash
# Demonstrate to_entries and from_entries
config='{"host": "db.local", "port": 5432, "ssl": true}'

echo "--- to_entries output ---"
echo "$config" | jq 'to_entries'

echo "--- round-trip back to object ---"
echo "$config" | jq 'to_entries | from_entries'

to_entries によるキーのフィルタリング

to_entries の最も実用的な用途の 1 つは、キー名自体に基づいて保持または削除するキーを動的にフィルタリングすることです。キー名を事前に把握できない場合、オブジェクト構築ではこの操作を行えません。

パターン:

  • to_entries | map(select(.key | test("regex"))) | from_entries
  • to_entries | map(select(.key != "secret")) | from_entries

次のスニペットは、設定オブジェクトを外部サービスに転送する前に、アンダースコアで始まるすべてのキー(内部用/プライベートフィールド)を削除します。

#!/usr/bin/env bash
raw_config='{
  "endpoint": "https://api.example.com",
  "timeout": 30,
  "_internalToken": "s3cr3t",
  "_debugMode": true,
  "retries": 3
}'

# Remove any key starting with underscore
echo "$raw_config" | jq '
  to_entries
  | map(select(.key | startswith("_") | not))
  | from_entries
'

to_entries によるキーの動的な名前変更

オブジェクト構築では、記述時にキー名がわかっている場合にキーの名前を変更できます。to_entries を使うと、キー名をプログラムで変更できます。たとえば、camelCase を snake_case に変換したり、プレフィックスを追加したりできます。

map の内部で各エントリの .key フィールドを更新し、その後 from_entries にパイプします。

  • map(.key |= gsub("(?<=[a-z])(?=[A-Z])"; "_") | .key |= ascii_downcase) — camelCase を snake_case に変換します
  • map(.key |= "app_" + .) — すべてのキーにプレフィックスを追加します

次の例では、コンテナに注入する前に、すべての環境変数名へ APP_ を付けて名前空間を分けます。

#!/usr/bin/env bash
env_vars='{"host": "localhost", "port": "8080", "debug": "false"}'

# Add APP_ prefix and uppercase all keys
echo "$env_vars" | jq '
  to_entries
  | map({ key: ("APP_" + (.key | ascii_upcase)), value })
  | from_entries
'

with_entries:便利な省略記法

to_entries | map(...) | from_entries というパターンは非常によく使われるため、jq には省略記法として with_entries(expr) が用意されています。

動作はまったく同じですが、より簡潔に記述できます。

  • with_entries(.value |= . * 2) — 数値の値をすべて 2 倍にします
  • with_entries(select(.value != null)) — 値が null のキーを削除します
  • with_entries(.key |= ascii_upcase) — すべてのキーを大文字にします

次のスニペットは、値が null または空文字列であるすべてのキーを削除します。これは REST API に PATCH リクエストを送信する前によく行うクリーンアップ処理です。

#!/usr/bin/env bash
patch_body='{
  "name": "Mehmet",
  "email": "",
  "phone": null,
  "city": "Istanbul"
}'

# Drop empty/null fields before PATCH
cleaned=$(echo "$patch_body" | jq '
  with_entries(select(.value != null and .value != ""))
')

echo "Cleaned payload:"
echo "$cleaned"

# In practice you would pipe to curl:
# curl -s -X PATCH https://api.example.com/users/1 \
#   -H "Content-Type: application/json" \
#   -d "$cleaned"

パイプラインでの map とオブジェクト構築の組み合わせ

実際の変換では、複数の jq 操作を連結します。API ペイロードを準備する一般的なパイプラインでは、次の処理を行います。

  1. map(select(...)) で入力配列をフィルタリングします
  2. オブジェクト構築で各要素の形を組み替えます
  3. 計算フィールドを追加します
  4. 結果を並べ替えます

次の例では、サーバーメトリクスの一覧を読み込み、CPU 使用率が高いサーバーだけを残して、Webhook に POST できる簡潔なアラートペイロードを生成します。

#!/usr/bin/env bash
metrics='[
  {"host": "web-01", "cpu": 23, "mem": 60, "region": "eu"},
  {"host": "web-02", "cpu": 91, "mem": 88, "region": "eu"},
  {"host": "db-01",  "cpu": 78, "mem": 95, "region": "us"},
  {"host": "db-02",  "cpu": 12, "mem": 40, "region": "us"}
]'

alerts=$(echo "$metrics" | jq '[
  .[] | select(.cpu > 75 or .mem > 85) | {
    server: .host,
    region,
    severity: (if .cpu > 90 or .mem > 90 then "critical" else "warning" end),
    metrics: { cpu: .cpu, mem: .mem }
  }
] | sort_by(.severity)')

echo "$alerts"

複数のソースから新しい JSON オブジェクトを構築する

jq では、加算演算子 + と as $var による変数束縛を使って、複数の JSON ソースから入力をマージし、オブジェクトを構築できます。

便利なパターン:

  • obj1 + obj2 — 2 つのオブジェクトをマージします(キーが競合する場合は右辺が優先されます)
  • --argjson — 2 つ目の JSON ドキュメントを変数として渡します
  • $ENV — jq の内部から環境変数を直接読み取ります

次のスニペットは、基本設定に環境固有の上書き設定をマージします。これは、シェルスクリプトで 12-factor アプリケーションの設定を管理する際によく使われるパターンです。

#!/usr/bin/env bash
base_config='{
  "logLevel": "info",
  "timeout": 30,
  "retries": 3,
  "database": "postgres://db.local/app"
}'

env_overrides='{
  "logLevel": "debug",
  "database": "postgres://db.staging/app_staging"
}'

# Merge: overrides win on conflicts
merged=$(echo "$base_config" | jq --argjson overrides "$env_overrides" '. + $overrides')

echo "Merged config:"
echo "$merged"

理解度チェック:to_entries と map

次の JSON オブジェクトがあり、値が 0 未満のキーをすべて削除して、0 以上の値だけを持つ新しいオブジェクトを作成するとします。これを正しく実行する jq 式はどれでしょうか。

入力:{"a": 10, "b": -3, "c": 0, "d": 5}

レッスンのまとめ:jq による JSON の変換

jq で JSON の形を組み替えるための基本的なテクニックを学びました。

  • オブジェクト構築 {} — 入力からフィールドを選択、名前変更、計算して新しいオブジェクトを作成します
  • map(expr) — ネストされたオブジェクト構築や、フィルタリング用の select() を含む任意の変換を、配列のすべての要素に適用します
  • to_entries / from_entries — オブジェクトを {key, value} の組の配列に変換してキーと値に配列操作を適用できるようにし、その後オブジェクトに戻します
  • with_entries(expr) — to_entries → map → from_entries という一連のパイプラインを簡潔に書くための省略記法です
  • + と --argjson によるオブジェクトのマージで、複数ソースのペイロードを扱います

これらの基本要素は組み合わせて使えます。select でフィルタリングし、オブジェクト構築で形を組み替え、計算フィールドで情報を加え、すべてを読みやすい 1 つの jq 式に連結できます。これらのパターンを習得すれば、Python や Node で専用スクリプトを書かなくても、シェルからほぼすべての JSON ペイロードを自在に扱えるようになります。

よくある質問

「jqによるJSONオブジェクトの変換と構築」レッスンは無料ですか?

はい。「jqによるJSONオブジェクトの変換と構築」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、DevOps Bootcampコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 DevOps Bootcampコースには全4レッスンが含まれています。

「jqによるJSONオブジェクトの変換と構築」で何を学びますか?

map、to_entries、オブジェクト構築を使ってデータを整形し、新しいJSONペイロードを生成します。 ブラウザで直接実行するハンズオンコードでDevOps Bootcampを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

DevOps Bootcampを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのDevOps Bootcampは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。

「jqによるJSONオブジェクトの変換と構築」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このDevOps Bootcampレッスンでコードを書いて実行できますか?

はい。すべてのDevOps Bootcampレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. jqパイプラインによるJSONのフィルタリングと選択
  2. jqによるJSONオブジェクトの変換と構築
  3. curlとjqを組み合わせたREST APIの利用
  4. yqによるYAML設定ファイルの編集
← DevOps Bootcampに戻る