データ形式・構造化データ
長いAPIレスポンスから必要な項目だけ取り出したい
使うツール
APIのレスポンスを見て、一覧にある商品名だけを会議用のメモへ移したい。最初は検索でnameを探してコピーできますが、途中に注文者のnameが出てきたり、一件だけ項目がなかったりすると、手作業は急に危うくなります。値の名前だけでなく、その値がどこに属しているかも指定できると便利です。
ここでは架空の商品一覧を使います。JSONPathはデータを書き換えるのではなく、指定した場所の値を選ぶために使います。最初から難しい式を一つ書こうとせず、全体、一覧、一件、名前という順に範囲を絞ると、空の結果が出ても原因を追いやすくなります。
URLではなく、取得済みの本文を用意する
このツールはAPIクライアントではありません。URLを貼ってデータを取得したり、ログインして次ページを探したりはしません。調べたいレスポンス本文を用意し、JSON部分だけを入力します。HTTPのステータス行やHeaderまで一緒に貼り付けると、JSONとして読めません。例を共有する場合は、実在する利用者名やTokenを含む本番レスポンスではなく、同じ構造の架空データへ置き換えると再現と説明がしやすくなります。
結果は、貼り付けた時点の入力に対するものです。元のAPIが後から更新されても、抽出結果が自動で最新になるわけではありません。値の鮮度も問題になる調査では、どの応答を調べたかを自分の作業記録へ残してください。取得元の内容確認と、入力済みJSONからの抽出を別の段階として扱います。
商品名と担当者名が同居するデータを用意する
{"items":[{"id":"A","name":"Notebook","price":500},{"id":"B","name":"Pencil","price":120},{"id":"C","name":null,"price":300}],"owner":{"name":"Demo team"},"page":1}JSONPath抽出を開き、JSON入力欄にこの例を入れます。別の式入力欄へ$だけを入れて実行すると、文書全体が一つの選択結果になります。この段階でエラーになるなら、式を複雑にする前にJSONの括弧や引用符を確認します。JSONと式を同じ欄へ続けて貼り付けると、一つのJSONとして読めなくなるため、入力先を分けてください。
結果の外側に配列が付いていても、元データが勝手に配列へ書き換わったわけではありません。このツールは選ばれた値の一覧として結果を返します。文書全体を一つ選んだなら、一件の結果を包む配列になります。『結果一覧の配列』と『元データのitems配列』を区別すると、その後の二重の角括弧にも戸惑いにくくなります。
最初の一件から、名前だけへ進む
$.items[0].name["Notebook"]$は文書全体、itemsはその直下の一覧、[0]は一覧の先頭、nameは選んだ商品の項目です。最初の要素は1ではなく0で指定します。[1]へ変えるとPencil、[2]ならnullが選ばれます。数字を変えるたびに元データの何番目を指しているか見比べると、単に式を暗記するより場所の読み方が身に付きます。
最初から名前へたどり着けないときは、$.itemsだけで一覧が取れるか、$.items[0]で最初の商品が取れるか、と一段ずつ試します。キー名は大文字小文字も含めて元データと一致させます。itemsをItemsへ直して試すのではなく、元データに実際に書かれている綴りを確認してください。結果が空であることだけを理由に、JSON側の名前を変更する必要はありません。
一件ずつコピーする作業を、*で置き換える
$.items[*].name["Notebook","Pencil",null][0]を[*]へ変えると、一覧の各要素へ同じ選択を適用できます。ここではitemsの中だけを対象にしているため、ownerのnameは混ざりません。全文検索でnameを拾う方法と違い、『商品の一覧にある名前』という条件を式そのものに残せます。あとで同じ形のレスポンスを調べるときにも、何を抜き出したのか説明しやすくなります。
ただし、結果が三件並んだことだけで、サーバーに商品が全部で三件しかないとは分かりません。例にはpageがありますが、このツールは次ページを取得しません。入力したレスポンスが一ページ分なら、調べられるのもその一ページ分です。全件の抽出が必要なときは、コピー元のAPIがページ分割や件数制限を持っていないかを別に確認します。
[]と[null]は、どちらも空に見えるけれど
$.items[2].nameの結果は[null]です。三件目にnameという項目は存在し、その値がnullだからです。一方、$.items[3].nameでは四件目が存在しないため[]になります。前者は一つの値を選べた状態、後者は選べた値がない状態です。空配列を『値がnullだった』と報告すると、データ不足なのか明示的な未設定なのかが伝わらなくなります。
{"items":[]}さらに、この別の例で$.itemsを選ぶと結果は[[]]になります。空の配列そのものを一つ選んだからです。$.items[*]なら、空の配列の中には要素がないので[]です。角括弧の数を数えるだけでは紛らわしくなりますが、『何を一件として選んだか』へ戻ると、結果の形を説明できます。空の結果でも、式が壊れているとは限りません。
名前と価格を別々に抜くと、対応がずれることがある
{"items":[{"id":"A","name":"Notebook","price":500},{"id":"B","price":120},{"id":"C","name":"Eraser","price":300}]}この例で名前だけを抜くとNotebookとEraserの二件、価格だけなら500、120、300の三件になります。名前の二行目と価格の二行目をそのまま横につなぐと、Eraserの価格を120だと誤って組み合わせます。存在しない項目の位置に、必ず空欄が補われると思わないでください。抽出した値の件数が違うことには、意味があります。
名前と価格の関係を保ちたいなら、$.items[*]で商品オブジェクトごと選び、idも一緒に確認します。表へ移したい場合も、独立した二つの値一覧を行番号だけで結び付けるのではなく、同じオブジェクトの項目を列へ変換する方法を選びます。JSON ⇔ CSV変換へ進むときは、欠けた項目やnullをどう表現するかも確認してください。
どこにでもあるnameを探す式は、調査向け
$..name最初のJSONへ戻してこの式を実行すると、商品名だけでなくDemo teamも見つかります。深い場所にある同名項目を探すには便利ですが、名前の由来が結果の値だけでは分かりにくくなります。最初の探索には広く探す式を使い、目的の階層が分かったら$.items[*].nameへ絞る、という使い分けができます。
すべてを再帰的に探す式を、必要以上に大きなデータへ使うのは避けます。このツールには結果件数などの上限があり、上限を超えた入力を少しだけ抜き出して完全な結果とするものではありません。先に必要な部分を含む小さな例で式を確かめ、その後に実際の入力へ適用すると、条件の間違いと処理量の問題を切り分けやすくなります。
ネットの例が動かないときは、対応範囲を確かめる
JSONPathには標準化された仕様と、実装ごとの拡張があります。SheepDevでは任意コードを実行せず、フィルターやスクリプト式を拒否する構成です。価格が一定以上の商品を選ぶ条件式を他のサイトからコピーしても、そのまま使えるとは限りません。未対応の式が失敗したことと、該当する商品が0件だったことを混同しないでください。
抽出を終えたら、何件の元データから何を選び、何件得られたかを確認します。結果だけを受け取る人には、使った式と対象範囲を添えると役立ちます。『商品名は二件』ではなく『このページのitemsからnameが存在する値を二件抽出した』と説明できれば、抜けや別階層の混入を後から追いやすくなります。