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

要点:エディタは場面で選びましょう。ユーティリティサイトとモバイルには 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());
}
}),
],
});
注意すべき四点:
json()がシンタックスハイライトと括弧の対応付けを提供する。自分で書かないこと。jsonParseLinter()が行・列レベルのエラーマーカーを生む。lintGutter()と組み合わせて行番号の隣にアイコンを表示する。updateListenerは必ずデバウンスする。十万行に対して打鍵のたびにJSON.parseを呼ぶと目に見えて遅延します。- 大きな文書には
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. デバッグのコツ
jq -eを表明として使う:式の結果がfalseかnullのとき終了コードが 1 になるので、そのままスクリプトのチェックに使えます。jq 'paths'がすべてのパスを列挙する。慣れない文書の構造を素早く俯瞰できます。jq '.. | objects | keys'はこれまで現れたすべてのキー名を見つけ、綴りの不統一を狩るのに便利です。- エディタで比較する前に整形する。整形されていない差分は純粋な雑音で、
/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 に任せてください。