JSONPathとは何か?
JSONPathは、JSONドキュメントから値を抽出するためのクエリ言語です。2007年にStefan GoessnerによってXMLにおけるXPathのアナログとして提案されたJSONPathは、ループや手動のトラバーサルコードを書くことなく、$.store.book[*].authorのような式でbookの配列内のすべてのauthorフィールドを選択できます。この言語は2024年2月にIETFによってRFC 9535として標準化されました。
JSONPath式は常にドキュメントのルートを表す$で始まります。そこからセレクターを連結します。オブジェクトキーにはドット記法、配列インデックスにはブラケット記法、すべての子要素にはワイルドカード、そしてネストの各レベルを検索する再帰降下演算子(..)を使用します。[?(@.price < 10)]のようなフィルター式を使えば、値に対して評価した条件に基づいて要素を選択できます。
JSONPathは、PostmanなどのAPIテストツール、DatadogやSplunkなどの可観測性プラットフォーム、Apache NiFiなどのワークフローエンジン、さらにJavaScript・Python・Go・Java・C#にわたるプログラミングライブラリで使用されています。コードや設定ファイルに組み込む前に実際のデータに対して式をテストすることで、JSON構造が想定と一致しない場合のサイレントエラーを防ぐことができます。
このJSONPathテスターを使う理由
JSONPath式を手書きするとエラーが発生しやすいです。ドットの欠落、ブラケットの種類の誤り、フィルター構文の間違いにより、エラーメッセージなしで空の結果が返されることがあります。このツールは、あなたの式が何に一致するかについて即座に視覚的フィードバックを提供します。
JSONPathのユースケース
JSONPath構文リファレンス
RFC 9535は標準のJSONPath構文を定義しています。下の表はほとんどのクエリで使用する演算子を網羅しています。すべての式は$(ルートノード)で始まり、1つ以上のセレクターを連結してドキュメント構造をナビゲートします。
| 演算子 | 説明 | 例 |
|---|---|---|
| $ | Root element | $.store |
| .key | Child property | $.store.book |
| [n] | Array index (zero-based) | $.store.book[0] |
| [*] | All elements in array/object | $.store.book[*] |
| .. | Recursive descent | $..author |
| [start:end] | Array slice | $.store.book[0:2] |
| [?()] | Filter expression | $.store.book[?(@.price<10)] |
| @ | Current node (inside filter) | $.store.book[?(@.isbn)] |
JSONPath vs jq vs XPath
JSONPath、jq、XPathはいずれも同じ問題(構造化データのクエリ)を、異なるフォーマットとユースケースに対して解決します。JSONPathはJSONを対象とし、ほとんどの言語でライブラリとして利用可能です。jqは独自のチューリング完全なフィルター言語を持つスタンドアロンのCLIツールです。XPathはXMLを操作し、W3C仕様スタックの一部です。
| 特徴 | JSONPath | jq | XPath |
|---|---|---|---|
| データ形式 | JSON | JSON | XML |
| 最初の要素へのアクセス | $.store.book[0] | .store.book[0] | /store/book[1] |
| 再帰検索 | $..price | .. | .price? | //price |
| フィルター式 | [?(@.price<10)] | select(.price < 10) | [price<10] |
| 仕様 | RFC 9535 | stedolan.github.io | W3C XPath 3.1 |
コード例
一般的な言語とツールでJSONPath式を評価する方法を示します。各例では比較のために同じbookstoreのJSON構造を使用しています。
import { JSONPath } from 'jsonpath-plus';
const data = {
store: {
book: [
{ title: 'Moby Dick', price: 8.99 },
{ title: 'The Great Gatsby', price: 12.99 }
]
}
};
// Get all book titles
JSONPath({ path: '$.store.book[*].title', json: data });
// → ['Moby Dick', 'The Great Gatsby']
// Recursive descent — find every price in the document
JSONPath({ path: '$..price', json: data });
// → [8.99, 12.99]
// Filter — books under $10
JSONPath({ path: '$.store.book[?(@.price < 10)]', json: data });
// → [{ title: 'Moby Dick', price: 8.99 }]from jsonpath_ng.ext import parse
import json
data = {
"store": {
"book": [
{"title": "Moby Dick", "price": 8.99},
{"title": "The Great Gatsby", "price": 12.99}
]
}
}
# Get all book titles
expr = parse("$.store.book[*].title")
titles = [match.value for match in expr.find(data)]
# → ['Moby Dick', 'The Great Gatsby']
# Recursive descent — all prices
expr = parse("$..price")
prices = [match.value for match in expr.find(data)]
# → [8.99, 12.99]
# Filter — books under $10
expr = parse("$.store.book[?price < 10]")
cheap = [match.value for match in expr.find(data)]
# → [{"title": "Moby Dick", "price": 8.99}]package main
import (
"fmt"
"github.com/ohler55/ojg/jp"
"github.com/ohler55/ojg/oj"
)
func main() {
src := `{
"store": {
"book": [
{"title": "Moby Dick", "price": 8.99},
{"title": "The Great Gatsby", "price": 12.99}
]
}
}`
obj, _ := oj.ParseString(src)
// Get all book titles
expr, _ := jp.ParseString("$.store.book[*].title")
results := expr.Get(obj)
fmt.Println(results)
// → [Moby Dick The Great Gatsby]
// Recursive descent — all prices
expr2, _ := jp.ParseString("$..price")
fmt.Println(expr2.Get(obj))
// → [8.99 12.99]
}# jq uses its own syntax, not JSONPath, but solves the same problem.
# Mapping common JSONPath patterns to jq:
# $.store.book[*].title → get all titles
echo '{"store":{"book":[{"title":"Moby Dick"},{"title":"Gatsby"}]}}' | \
jq '.store.book[].title'
# → "Moby Dick"
# → "Gatsby"
# $..price → recursive descent for "price" keys
echo '{"a":{"price":1},"b":{"price":2}}' | \
jq '.. | .price? // empty'
# → 1
# → 2
# Filter: books where price < 10
echo '{"store":{"book":[{"title":"A","price":8},{"title":"B","price":12}]}}' | \
jq '.store.book[] | select(.price < 10)'
# → {"title":"A","price":8}