コンテンツにスキップ
LinkedInX

PythonでのJSON処理

約5分

対象読者: Pythonの基本を理解していてAPIレスポンスの処理を学びたい方

JSON(JavaScript Object Notation)とは、データをテキスト形式で表現するための軽量なデータフォーマットです。APIのレスポンス、設定ファイル、ログなど、エンジニアリングのあらゆる場面でJSONが使われます。PythonにはJSONを扱う標準ライブラリが組み込まれており、さらにpydanticを使うことで型安全なデータ処理が実現できます。

JSONは次のような形式でデータを表現します。

{
  "name": "Alice",
  "age": 30,
  "is_active": true,
  "tags": ["python", "ai", "engineer"],
  "address": {
    "city": "Tokyo",
    "zip": "100-0001"
  },
  "notes": null
}

PythonとJSONの型の対応関係:

JSONの型Pythonの型
objectdict{"key": "value"}
arraylist[1, 2, 3]
stringstr"hello"
numberint / float42, 3.14
true/falseTrue / FalseTrue
nullNoneNone

Pythonの標準ライブラリ json で、文字列とPythonオブジェクトの相互変換ができます。

json.loads():JSON文字列をPythonオブジェクトに変換

Section titled “json.loads():JSON文字列をPythonオブジェクトに変換”
import json

# JSON文字列(APIから受け取ったレスポンスなど)
json_string = '{"name": "Alice", "age": 30, "is_active": true}'

# Pythonの辞書に変換する
data = json.loads(json_string)

print(type(data))         # <class 'dict'>
print(data["name"])       # Alice
print(data["age"])        # 30
print(data["is_active"])  # True(Pythonのbool型)

json.dumps():PythonオブジェクトをJSON文字列に変換

Section titled “json.dumps():PythonオブジェクトをJSON文字列に変換”
import json

data = {
    "model": "claude-opus-4-5",
    "max_tokens": 1024,
    "messages": [
        {"role": "user", "content": "こんにちは"}
    ]
}

# Pythonの辞書をJSON文字列に変換する
json_string = json.dumps(data)
print(json_string)
# {"model": "claude-opus-4-5", "max_tokens": 1024, "messages": [{"role": "user", "content": "こんにちは"}]}

# 日本語をそのまま出力する(ensure_ascii=False)
json_string_readable = json.dumps(data, ensure_ascii=False, indent=2)
print(json_string_readable)
# {
#   "model": "claude-opus-4-5",
#   "max_tokens": 1024,
#   "messages": [
#     {
#       "role": "user",
#       "content": "こんにちは"
#     }
#   ]
# }
import json

# JSONファイルに書き込む
data = {"version": "1.0", "settings": {"theme": "dark", "language": "ja"}}

with open("config.json", "w", encoding="utf-8") as f:
    json.dump(data, f, ensure_ascii=False, indent=2)

# JSONファイルを読み込む
with open("config.json", "r", encoding="utf-8") as f:
    loaded = json.load(f)

print(loaded["settings"]["theme"])  # dark

APIレスポンスはネストした構造になることが多いです。

import json

# GitHub APIのレスポンスを模した例
response_json = """
{
  "id": 12345,
  "name": "my-project",
  "owner": {
    "login": "alice",
    "type": "User"
  },
  "topics": ["ai", "python", "api"],
  "license": {
    "name": "MIT License",
    "spdx_id": "MIT"
  },
  "private": false,
  "stargazers_count": 128,
  "homepage": null
}
"""

repo = json.loads(response_json)

# ネストしたデータへのアクセス
print(repo["owner"]["login"])         # alice
print(repo["topics"][0])              # ai
print(repo["license"]["name"])        # MIT License

# 存在しないかもしれないキーは get() で安全に取得する
homepage = repo.get("homepage")       # None(KeyError が発生しない)
description = repo.get("description", "説明なし")  # デフォルト値を指定できる
print(homepage)     # None
print(description)  # 説明なし
import json

# ユーザーリストを模したJSON
users_json = """
[
  {"id": 1, "name": "Alice", "role": "admin"},
  {"id": 2, "name": "Bob",   "role": "user"},
  {"id": 3, "name": "Carol", "role": "user"}
]
"""

users = json.loads(users_json)

# 全ユーザーの名前を取り出す
names = [user["name"] for user in users]
print(names)  # ['Alice', 'Bob', 'Carol']

# ロールで絞り込む
admins = [user for user in users if user["role"] == "admin"]
print(admins)  # [{'id': 1, 'name': 'Alice', 'role': 'admin'}]

1. 文字列とオブジェクトを混同する

Section titled “1. 文字列とオブジェクトを混同する”
import json

# json.loads() を忘れると文字列のまま
raw = '{"name": "Alice"}'
print(raw["name"])  # TypeError!文字列にインデックスアクセスはできない

data = json.loads(raw)
print(data["name"])  # OK: Alice

2. json.loads() と json.load() の使い分け

Section titled “2. json.loads() と json.load() の使い分け”
import json

# loads() → 文字列から変換(l + string の "s")
data = json.loads('{"key": "value"}')

# load() → ファイルオブジェクトから変換
with open("data.json") as f:
    data = json.load(f)
import json

data = {"message": "こんにちは世界"}

# デフォルトではUnicodeエスケープされる
print(json.dumps(data))
# {"message": "こんにちは世界"}

# ensure_ascii=False で日本語をそのまま出力する
print(json.dumps(data, ensure_ascii=False))
# {"message": "こんにちは世界"}

pydanticを使った型安全なJSON処理

Section titled “pydanticを使った型安全なJSON処理”

pydantic はPythonの型ヒントを使って、データのバリデーションと型変換を自動で行うライブラリです。APIレスポンスを扱う際に特に便利です。

pip install pydantic

BaseModelでデータモデルを定義する

Section titled “BaseModelでデータモデルを定義する”
from pydantic import BaseModel, Field
from typing import Optional

# データモデルを定義する
class Owner(BaseModel):
    login: str
    type: str

class Repository(BaseModel):
    id: int
    name: str
    owner: Owner
    topics: list[str] = []              # デフォルト値
    private: bool = False
    stargazers_count: int = 0
    homepage: Optional[str] = None      # Noneを許可する
    description: Optional[str] = None

# 辞書からモデルを作成する
data = {
    "id": 12345,
    "name": "my-project",
    "owner": {"login": "alice", "type": "User"},
    "topics": ["ai", "python"],
    "stargazers_count": 128,
    "homepage": None,
    "private": False
}

repo = Repository(**data)

# 型安全にアクセスできる
print(repo.name)              # my-project
print(repo.owner.login)       # alice(ネストしたモデルも自動変換)
print(repo.topics)            # ['ai', 'python']
print(repo.homepage)          # None

バリデーションエラーを捕捉する

Section titled “バリデーションエラーを捕捉する”
from pydantic import BaseModel, ValidationError

class User(BaseModel):
    id: int
    name: str
    age: int

# 不正なデータを渡してみる
try:
    user = User(id="not-an-int", name="Alice", age=30)
except ValidationError as e:
    print(e)
    # 1 validation error for User
    # id
    #   Input should be a valid integer, unable to parse string as an integer
    #   [type=int_parsing, input_value='not-an-int', ...]

型が合わない場合に、明確なエラーメッセージが出ます。

APIレスポンスをpydanticでパースする実践例

Section titled “APIレスポンスをpydanticでパースする実践例”
import requests
from pydantic import BaseModel
from typing import Optional

class GitHubUser(BaseModel):
    login: str
    id: int
    name: Optional[str] = None
    company: Optional[str] = None
    public_repos: int = 0
    followers: int = 0

def get_github_user(username: str) -> GitHubUser | None:
    """GitHubユーザー情報を取得してpydanticモデルとして返す"""
    try:
        response = requests.get(
            f"https://api.github.com/users/{username}",
            headers={"Accept": "application/vnd.github.v3+json"},
            timeout=10
        )
        response.raise_for_status()

        # レスポンスの辞書をpydanticモデルに変換する
        # モデルに定義されていないフィールドは無視される
        return GitHubUser(**response.json())

    except requests.exceptions.RequestException as e:
        print(f"API呼び出しエラー: {e}")
        return None

# 実行
user = get_github_user("torvalds")
if user:
    print(f"名前: {user.name}")
    print(f"公開リポジトリ数: {user.public_repos}")
    print(f"フォロワー数: {user.followers:,}")

model.json() または model.model_dump() で再びJSONや辞書に戻せます:

user_dict = user.model_dump()
user_json = user.model_dump_json()
  • json.loads() でJSON文字列を辞書に変換し、json.dumps() で辞書をJSON文字列に変換する
  • ファイル操作には json.load() / json.dump() を使う
  • 日本語を含む場合は ensure_ascii=False を付ける
  • pydantic のBaseModelを使うと型安全なデータ処理とバリデーションができる
  • APIレスポンスの辞書を Model(**response.json()) でpydanticモデルに変換する

次のステップとして、Python × AI SDK実践でAnthropicのAPIレスポンスをpydanticと組み合わせて扱う方法を学びましょう。

Q: json モジュールとpydanticはどう使い分けますか?

A: シンプルな設定ファイルの読み書きには json モジュールで十分です。APIレスポンスの処理や、型チェック・バリデーションが必要な場合はpydanticが有効です。FastAPIはpydanticを内部で使っており、自然に統合できます。

Q: JSONに含まれる日付(例: “2026-05-13T09:00:00Z”)はどう扱いますか?

A: json.loads() はISO 8601形式の日付文字列を自動でdatetimeに変換しません。pydanticでは datetime 型を指定すると自動変換されます。標準ライブラリを使う場合は datetime.fromisoformat() で変換できます。

Q: 大きなJSONファイルを効率的に処理するには?

A: 数百MBを超えるJSONファイルは、json.load() で全体をメモリに読み込むのではなく、ijson ライブラリを使ったストリーミング処理が有効です。

Q: pydantic v1とv2は何が違いますか?

A: pydantic v2(2023年リリース)ではAPIが変更されました。dict()model_dump() に、json()model_dump_json() になりました。新規プロジェクトではv2を使うことを推奨します。

このページの外部仕様・背景情報は、参考文献を参照してください。[1][2]

  1. Python Software Foundation, The Python Tutorial
  2. OpenAI, OpenAI API documentation
クイズ