json / query

JSONパスクエリ

JSONPathクエリ($.foo.bar$..price)をブラウザ内で実行・デバッグ。入力データはサーバーに送信されません。

サンプルクエリ

使い方

01
  1. 上部のテキストエリアにJSONをペーストしてください
  2. クエリ入力欄にJSONPathを入力します(例: $.store.book[*].title
  3. サンプルクエリボタンをクリックするとクエリが自動入力されます
  4. 「実行」ボタン(またはEnterキー)でクエリを実行します
  5. 下部のターミナルにマッチ件数と各結果が表示されます

実装コード

02

外部ライブラリを使わず、標準JavaScriptのみで実装したJSONPathエンジンのコアロジックです。 パス式をセグメント列に分解し、再帰的にノードを辿ることで再帰検索(..)も実現しています。

export type QueryResult =
  | { ok: true; results: unknown[]; count: number }
  | { ok: false; error: string }

// セグメント適用(再帰)
function applySegments(node: unknown, segments: Segment[]): unknown[] {
  if (segments.length === 0) return [node]
  const [seg, ...rest] = segments

  if (seg.type === 'key') {
    if (node !== null && typeof node === 'object' && !Array.isArray(node)) {
      const obj = node as Record<string, unknown>
      if (Object.prototype.hasOwnProperty.call(obj, seg.key)) {
        return applySegments(obj[seg.key], rest)
      }
    }
    return []
  }

  if (seg.type === 'index') {
    if (Array.isArray(node)) {
      const idx = seg.index < 0 ? node.length + seg.index : seg.index
      if (idx >= 0 && idx < node.length) {
        return applySegments(node[idx], rest)
      }
    }
    return []
  }

  if (seg.type === 'wildcard') {
    if (Array.isArray(node)) {
      return node.flatMap(item => applySegments(item, rest))
    }
    if (node !== null && typeof node === 'object') {
      return Object.values(node as Record<string, unknown>)
        .flatMap(v => applySegments(v, rest))
    }
    return []
  }

  if (seg.type === 'recursive') {
    const results: unknown[] = []
    if (seg.key === null) {
      results.push(...applySegments(node, rest))
    } else if (
      node !== null && typeof node === 'object' && !Array.isArray(node)
    ) {
      const obj = node as Record<string, unknown>
      if (Object.prototype.hasOwnProperty.call(obj, seg.key)) {
        results.push(...applySegments(obj[seg.key], rest))
      }
    }
    if (Array.isArray(node)) {
      for (const item of node) results.push(...applySegments(item, segments))
    } else if (node !== null && typeof node === 'object') {
      for (const val of Object.values(node as Record<string, unknown>)) {
        results.push(...applySegments(val, segments))
      }
    }
    return results
  }

  return []
}

JSONPath記法ガイド

03
$ — ルートを表す記号
すべてのJSONPathは $ から始まります。$ 単体でJSONのルート全体を返します。XPathの / に相当し、絶対パスで書くのが基本です。
.. — 再帰的検索(descendant-or-self)
$..price のように .. を使うと、JSON全体を再帰的に探索してキーにマッチするすべての値を返します。ネストの深さに関係なく一括収集できるため、APIレスポンス内の特定フィールドを全取得したいときに便利です。
[*] — 配列またはオブジェクトの全要素
$.book[*] で配列の全要素を取得できます。$.store[*] のようにオブジェクトに使うと全値(book配列とbicycleオブジェクト)を返します。[*] の後ろにプロパティを続けて $.book[*].title のように使うのが典型的なパターンです。
[-1] — 後ろからのインデックス
[-1] で配列の最後の要素、[-2] で後ろから2番目の要素を取得できます。Pythonのスライス記法と同様の感覚で使えます。ただしJSON Pathの仕様実装によって動作が異なる場合があるため、本番コードで使う際は動作確認を推奨します。
このツールの実装範囲について
フィルタ式($.book[?(@.price < 10)])、スライス記法([0:2])、複数インデックス指定([0,2])は本実装では対応していません。これらが必要な場合はjsonpath-plusなどのライブラリを検討してください。

関連ツール

04
JSONフォーマッターJSON to TypeScriptCSV ↔ JSON変換

ソースコード

05

このツールのソースコード(テストコードを含む)はGitHubで公開しています。 MITライセンスで自由に利用・改変できます。

GitHub でコードを見る →