APIからJSONが返ってきても、どの角括弧からたどればよいか分からないことがあります。最初に一番外側の形を見て、その次に必要な項目の有無と型を確認すると、エラーの場所を追いやすくなります。
JSONとPythonの対応
JSONのオブジェクトはPythonのdict、配列はlistに変換されます。文字列はstr、nullはNoneです。JSONの文字列そのものと、json.loadsやjson.loadで読み取った値は別なので、読む前の文字列へ辞書の操作をしないようにします。
次は実APIの応答ではなく、説明用の架空データです。response.jsonとしてUTF-8で保存します。
{
"items": [{"name": "東京", "count": 3}, {"name": "大阪", "count": 2}],
"next": null
}
外側がオブジェクトで、itemsの中に配列があります。最初の名前ならpayload["items"][0]["name"]です。ただし、常に一件以上あるとは限らないので、配列が空の場合も想定します。
読み取りから保存までのコード
次をread_response.pyとして同じフォルダーで実行します。通信は行いません。
import json
with open("response.json", encoding="utf-8") as src:
payload = json.load(src)
if not isinstance(payload, dict):
raise ValueError("外側はオブジェクトを想定しています")
items = payload.get("items")
if not isinstance(items, list):
raise ValueError("itemsが配列ではありません")
for item in items:
if not isinstance(item, dict):
raise ValueError("項目がオブジェクトではありません")
count = item.get("count")
if type(count) is not int or count < 0:
raise ValueError("countは0以上の整数にしてください")
with open("checked.json", "x", encoding="utf-8") as out:
json.dump(payload, out, ensure_ascii=False, indent=2, allow_nan=False)
print(len(items), sum(item["count"] for item in items))
この入力なら、画面に2と5が出ます。checked.jsonは日本語を読める形で整形して保存されます。ensure_ascii=Falseと、ファイルのencodingは別の指定です。allow_nan=FalseはNaNなどをJSONの値として書き出さない指定です。
countの条件はこの架空データ用に決めたものです。実際のAPIでは、負数や小数、文字列の数値を認める場合もあるので、仕様に合わせて確認方法を変えます。boolと整数を区別するため、ここではtype(count) is intを使っています。
エラーを分けて読む
JSONDecodeErrorなら、JSONの書式を確認します。括弧の欠け、末尾の余計なカンマ、JSONの前後に付いた説明文などが手がかりになります。KeyErrorやTypeErrorなら、読み取った後の項目名や型を調べます。エラーを隠すために、存在しない金額へ0を入れて続ける処理は避けます。
JSONとして読めたことは、API処理の成功とは別です。エラー情報がJSONで返るサービスもあります。HTTPの状態、サービス固有の成功条件、必要な項目の順に確認してください。
保存する内容を選ぶ
応答全文を残すと、認証情報や業務データを保存してしまう場合があります。実データでは、残す目的と共有先を決め、必要な項目だけに絞ります。例では入力全体を保存していますが、業務への導入時にそのまま採用する条件ではありません。
関連する作業
APIの取得段階と、型を使ってコードを読みやすくする方法は次の記事で補えます。
