JSON Toolkit

エディタ、コマンドライン、自動化ツールチェーン

要点:エディタは「正しく見て、正しく編集する」を解き、コマンドラインは「バッチで再現可能」を解きます。どちらにも十年使える道具がいくつかあります。

初級約 10 分で読めます2026-10-30 公開2026-10-30 更新
エディタ、コマンドライン、自動化ツールチェーンのカバー画像

要点:エディタは場面で選びましょう。ユーティリティサイトとモバイルには CodeMirror 6、完全な IDE 体験が必要なときだけ Monaco。バッチ作業は jq と構文・構造・スタイルの三つの CI チェックに任せ、引数は常に jq --arg で渡します。

JSON を手で確認する時代は終わるべきです。機械が繰り返せることは、人間の目に任せないでください。

1. ブラウザのエディタ

1.1 最小限の CodeMirror 6 統合

import { EditorView, basicSetup } from 'codemirror';
import { json, jsonParseLinter } from '@codemirror/lang-json';
import { linter, lintGutter } from '@codemirror/lint';

const view = new EditorView({
  parent: document.getElementById('editor')!,
  doc: '{"hello": "json"}',
  extensions: [
    basicSetup,
    json(),
    linter(jsonParseLinter()),
    lintGutter(),
    EditorView.updateListener.of((update) => {
      if (update.docChanged) {
        // 重い処理の前にデバウンスする。打鍵ごとに整形しない
        scheduleParse(update.state.doc.toString());
      }
    }),
  ],
});

注意すべき四点:

  1. json() がシンタックスハイライトと括弧の対応付けを提供する。自分で書かないこと。
  2. jsonParseLinter() が行・列レベルのエラーマーカーを生む。lintGutter() と組み合わせて行番号の隣にアイコンを表示する。
  3. updateListener は必ずデバウンスする。十万行に対して打鍵のたびに JSON.parse を呼ぶと目に見えて遅延します。
  4. 大きな文書には EditorView.lineWrapping 以上の最適化が必要で、全体を一度に描画しないようにします(CodeMirror 6 は既定でビューポート単位に描画し、旧バージョンより改善されています)。

1.2 折りたたみとアウトライン

CodeMirror 6 は折りたたみを内蔵し、JSON の場面では通常「オブジェクト/配列」単位で折りたたみます。

import { foldGutter, foldKeymap } from '@codemirror/language';
import { keymap } from '@codemirror/view';

foldGutter() と keymap.of(foldKeymap) で、ユーザーはキーボードショートカットで長い配列を畳めます。

1.3 CodeMirror と Monaco の選択

観点 CodeMirror 6 Monaco
バンドルサイズ 小さくモジュール式 大きい(数 MB)
モバイル 十分対応 体験は平凡
言語サービス 自分で構築する必要 豊富で組み込み
カスタマイズコスト 低い 中程度

ユーティリティサイトとモバイル優先 → CodeMirror。完全な IDE 体験(複数ファイル、定義ジャンプ、補完)→ Monaco。

2. コマンドライン:よく使う jq レシピ六つ

jq は JSON にとっての sed + awk です。構文のごく一部を学ぶだけで日常の八割をカバーできます。

# 1. フィールドを選ぶ(最も一般的)
jq '.data.items[].name' response.json

# 2. フィルタ + 再形成
jq '.data.items[] | select(.price < 10) | {id, price}' response.json

# 3. 集計(length、sum、max)
jq '[.items[] | .price] | {count: length, total: add, max: max}' response.json

# 4. グループ化と集計
jq '[.items[]] | group_by(.category) | map({category: .[0].category, count: length})' response.json
# 5. 複数ファイルを一つの配列にマージ
jq -s '.' part-*.json > merged.json

# 6. JSON Lines を行ごとに処理
jq -c 'select(.level == "error")' app.jsonl

# よく使う出力スイッチ
jq -r '.data.name'      # 引用符を剥がし、シェルへ綺麗にパイプできるようにする
jq -c '.'               # コンパクト出力、一行
jq --arg id "$ID" '.id == $id'   # 引数を安全に渡し、連結による注入を避ける

--arg は特筆に値します。変数を式に直接連結すると、値に引用符が含まれた瞬間に壊れますが、--arg なら jq がエスケープを扱います。

2.1 他のツール一覧

ツール 目的 一文で
yq YAML/JSON/XML 変換 jq に似た構文で、設定ファイルの主力
dasel 形式をまたぐ統合クエリ JSON/YAML/TOML/XML/CSV に対応
fx インタラクティブな JSON 閲覧 ターミナルでクリックしながら探索
jless ターミナルでのページ閲覧 less のように、折りたたみと検索付き
gron JSON を grep 可能な行に平坦化 「このフィールドはどの階層か」を突き止めるのに最適
ajv-cli スキーマ検証 CI で JSON ファイルを検証

gron の発想は特に知る価値があります。

$ echo '{"a":{"b":[1,2]}}' | gron
json = {};
json.a = {};
json.a.b = [];
json.a.b[0] = 1;
json.a.b[1] = 2;

平坦化すれば grep でパスを特定でき、エディタで一段ずつ展開するよりずっと速いです。

3. CI における検証の三層

層 ツール 失敗が意味すること
構文 JSON.parse / jq empty ファイル自体が壊れている
構造 ajv-cli + スキーマ フィールド欠落、型違い
スタイル Prettier 整形の不統一、差分の雑音
# 構文:リポジトリ内のすべての json ファイルを一括検証
find . -name '*.json' -not -path './node_modules/*' -print0 \
  | xargs -0 -n1 jq empty

# 構造:スキーマに対して検証
npx ajv-cli validate -s schemas/order.json -d 'fixtures/orders/*.json' --strict=false

# スタイル:検査のみ、変更しない
npx prettier --check "**/*.json"

三層すべてが必要です。エラーメッセージがまったく異なるからです。 jq empty は「どの行が壊れているか」しか言わず、ajv は「どのフィールドがどのキーワードに違反したか」を言い、Prettier は「整形が不統一」としか言いません。

4. pre-commit フック

# .husky/pre-commit の例(抜粋)
npx lint-staged
{
  "lint-staged": {
    "*.json": ["prettier --write", "jq empty"]
  }
}

package.json や tsconfig.json のようなツール設定ファイルには注意してください。Prettier に気軽に再インデントさせないこと。 一部のツールは整形に敏感です(正しい JSON なら敏感であるはずがありませんが、実際に問題を起こしてきました)。個別に除外できます。

5. デバッグのコツ

  1. jq -e を表明として使う:式の結果が false か null のとき終了コードが 1 になるので、そのままスクリプトのチェックに使えます。
  2. jq 'paths' がすべてのパスを列挙する。慣れない文書の構造を素早く俯瞰できます。
  3. jq '.. | objects | keys' はこれまで現れたすべてのキー名を見つけ、綴りの不統一を狩るのに便利です。
  4. エディタで比較する前に整形する。整形されていない差分は純粋な雑音で、/diff のような構造比較の方が正確です。

6. チェックリスト

  • [ ] エディタのエラーマーカーが行と列まで正確で、パースがデバウンスされている。
  • [ ] チームのスクリプトが文字列連結ではなく jq --arg で引数を渡す。
  • [ ] CI に三つの検証層(構文/構造/スタイル)がある。
  • [ ] pre-commit はステージ済みファイルだけを処理し、全体走査がコミットを遅くしない。
  • [ ] ツールのバージョンが package.json か .tool-versions で固定されている。

FAQ

CodeMirror 6 と Monaco はどう選べばよいですか?

ユーティリティサイトとモバイルには CodeMirror 6 を優先してください。小さく、モジュール式で、モバイル対応がよく、シンタックスハイライト、括弧の対応付け、折りたたみを必要に応じて組み合わせられます。完全な IDE 体験(複数ファイル、定義ジャンプ、補完)が必要なときだけ Monaco に手を伸ばします。代償は数 MB のバンドルと高いカスタマイズ工数です。

CI で構文・構造・スタイルの三つが必要なのはなぜですか?

三層はまったく異なるエラーを報告します。jq empty はどの行が壊れているかを言い、ajv はどのフィールドがどのキーワードに違反したかを言い、Prettier は整形が不統一としか言いません。一つを落とすと他のエラーを誤読しやすくなるので、三つすべてを保ち、層が失敗したらすぐ止めます。

CI で検証する代わりに pre-commit に頼るべきですか?

いいえ。フックはステージ済みファイルだけを処理するので、一分以内のフィードバックは得られますが、リポジトリ全体やコミットを迂回した変更をカバーできません。完全性は CI の責任です。pre-commit はステージ済みファイル、全体走査は CI に任せてください。