JSON(JavaScript Object Notation)とは、データをテキスト形式で表現するための軽量なデータフォーマットです。APIのレスポンス、設定ファイル、ログなど、エンジニアリングのあらゆる場面でJSONが使われます。PythonにはJSONを扱う標準ライブラリが組み込まれており、さらにpydanticを使うことで型安全なデータ処理が実現できます。
JSONとは
Section titled “JSONとは”JSONは次のような形式でデータを表現します。
{
"name": "Alice",
"age": 30,
"is_active": true,
"tags": ["python", "ai", "engineer"],
"address": {
"city": "Tokyo",
"zip": "100-0001"
},
"notes": null
}PythonとJSONの型の対応関係:
| JSONの型 | Pythonの型 | 例 |
|---|---|---|
| object | dict | {"key": "value"} |
| array | list | [1, 2, 3] |
| string | str | "hello" |
| number | int / float | 42, 3.14 |
| true/false | True / False | True |
| null | None | None |
json モジュールの基本操作
Section titled “json モジュールの基本操作”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": "こんにちは"
# }
# ]
# }ファイルへの読み書き
Section titled “ファイルへの読み書き”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ネストしたJSONの扱い
Section titled “ネストしたJSONの扱い”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) # 説明なしリストのJSONを処理する
Section titled “リストのJSONを処理する”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'}]よくある落とし穴
Section titled “よくある落とし穴”1. 文字列とオブジェクトを混同する
Section titled “1. 文字列とオブジェクトを混同する”import json
# json.loads() を忘れると文字列のまま
raw = '{"name": "Alice"}'
print(raw["name"]) # TypeError!文字列にインデックスアクセスはできない
data = json.loads(raw)
print(data["name"]) # OK: Alice2. 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)3. 日本語の文字化け
Section titled “3. 日本語の文字化け”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 pydanticBaseModelでデータモデルを定義する
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と組み合わせて扱う方法を学びましょう。
よくある質問
Section titled “よくある質問”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]
- Python Software Foundation, The Python Tutorial
- OpenAI, OpenAI API documentation