JSONはWeb APIや設定ファイルで広く使われるデータ形式です。
Pythonには標準でjsonモジュールがあり、辞書やリストを簡単に保存・読み込みできます。
本記事ではjson.dump/json.loadとjson.dumps/json.loadsの基本から、日本語の扱い、整形、よくあるエラーまでを、Python初心者の方に向けて丁寧に解説します。
JSONとPythonのjsonモジュールの基本
Pythonの辞書・リストはJSONにそのまま対応
Pythonのdict
(辞書)やlist
(リスト)は、JSONのobject
やarray
にそのまま対応します。
つまり、普段のPythonのデータ構造をほぼそのままJSONに書き出し、また読み戻せます。
まずは最小の例を見てみましょう。
# Pythonの辞書をJSON文字列に変換して表示する例
import json
data = {
"title": "入門",
"tags": ["python", "json"],
"published": True,
"count": 3
}
# ensure_ascii=False で日本語をそのまま出力、indent=2 で見やすく整形
json_str = json.dumps(data, ensure_ascii=False, indent=2)
print(json_str)
{
"title": "入門",
"tags": [
"python",
"json"
],
"published": true,
"count": 3
}
上の例では、PythonのTrue
がJSONのtrue
に、整数3が3
に変換されています。
JSONではブール値が小文字(true/false)、nullはnull
になる点に注意してください。
Pythonの型とJSONの対応
Pythonの基本的な型は、JSONの基本型に素直に写像されます。
対応関係を表にまとめます。
Pythonの値/型 | JSONの型 | 例 | 備考 |
---|---|---|---|
dict | object | {“a”: 1} | キーは原則文字列(JSON仕様) |
list | array | [1, 2, 3] | 可変長の順序付き |
tuple | array | [1, 2] | JSONではリスト扱い(後述) |
str | string | “hello” | 文字列はダブルクォート |
int/float | number | 42, 3.14 | 整数・実数ともnumber |
bool | boolean | true/false | 小文字で表記 |
None | null | null | そのままnull |
その他(例: set, bytes) | 非対応 | — | 直接は変換不可 |
非対応の型はそのままでは保存できません。
まずは辞書・リスト・文字列・数値・ブール・Noneの基本6種を使うのが安全です。
動作確認を簡単に行ってみましょう。
# JSONが数値・bool・nullをどう扱うかを確認
import json
data = {"ok": True, "nothing": None, "pi": 3.14}
s = json.dumps(data) # 文字列化
print(s) # JSON文字列
obj = json.loads(s) # Pythonオブジェクトへ戻す
print(type(obj["ok"]), obj["ok"])
print(type(obj["nothing"]), obj["nothing"])
print(type(obj["pi"]), obj["pi"])
{"ok": true, "nothing": null, "pi": 3.14}
<class 'bool'> True
<class 'NoneType'> None
<class 'float'> 3.14
JSONにコメントは不可
JSONにはコメントを書けません。
よくあるミスとして// コメント
や/* ... */
を入れてしまうケースがありますが、これはjson.loads
でもjson.load
でもJSONDecodeError
になります。
# コメント付きJSONはエラーになる例
import json
json_text = """
{
"name": "Taro", // ここはコメント(エラー)
"age": 20
}
"""
try:
json.loads(json_text)
except json.JSONDecodeError as e:
print("JSONDecodeError:", e)
JSONDecodeError: Expecting property name enclosed in double quotes: line 3 column 19 (char 21)
メモを書きたい場合は別ファイルに書くか、専用のキー(例: “_comment”)を自分で定めて文字列として入れるなどの工夫をしましょう。
ファイルに書く・読む json.dumpとjson.load
ファイルに保存するにはjson.dump
、ファイルから読み込むにはjson.load
を使います。
dump/loadは「ファイルオブジェクト」相手、dumps/loadsは「文字列」相手という対応を覚えると混乱しません。
辞書やリストをJSONファイルに保存する
with open(..., "w", encoding="utf-8")
でファイルを開き、json.dump
にファイルオブジェクトを渡します。
日本語を崩さず保存するためensure_ascii=False
を付けるのがポイントです。
# users.json に辞書(リストを含む)を保存する
import json
users = {
"users": [
{"id": 1, "name": "山田太郎"},
{"id": 2, "name": "Jane Doe"}
],
"active": True
}
# UTF-8で書き込み、ensure_ascii=Falseで日本語をそのまま保存
with open("users.json", "w", encoding="utf-8") as f:
json.dump(users, f, ensure_ascii=False, indent=2)
print("書き込み完了: users.json")
書き込み完了: users.json
JSONファイルからPythonのデータに戻す
先ほど保存したusers.json
を読み込み、辞書として扱えることを確かめます。
# users.json を読み込んで中身と型を確認
import json
with open("users.json", "r", encoding="utf-8") as f:
loaded = json.load(f)
print(type(loaded)) # dictになる
print(loaded["users"][0]) # 最初のユーザー
print("active =", loaded["active"])
<class 'dict'>
{'id': 1, 'name': '山田太郎'}
active = True
読み込んだ瞬間から通常のPythonの辞書/リストとして扱えるので、キーアクセスやループ、条件分岐などがそのままできます。
日本語を正しく保存する
json.dumps
/json.dump
は既定でensure_ascii=True
です。
これだと日本語が\u3053
のようなエスケープシーケンスになります。
ファイルとして人間が読める形にしたいときはensure_ascii=False
を指定します。
# ensure_ascii の違いを比較
import json
data = {"text": "こんにちは", "emoji": "🍣"}
s_default = json.dumps(data) # 既定(ensure_ascii=True)
s_utf8 = json.dumps(data, ensure_ascii=False)
print("default:", s_default)
print("utf8 :", s_utf8)
default: {"text": "\u3053\u3093\u306b\u3061\u306f", "emoji": "\ud83c\udf63"}
utf8 : {"text": "こんにちは", "emoji": "🍣"}
整形して見やすくする
読みやすいJSONにしたい場合はindent
でインデント幅、sort_keys=True
でキーをアルファベット順に整列できます。
開発中は整形、配布時は圧縮と使い分けると便利です。
# 整形(Pretty Print)と圧縮(Minify)の例
import json
data = {"z": 1, "a": [3, 2, 1]}
pretty = json.dumps(data, ensure_ascii=False, indent=2, sort_keys=True)
compact = json.dumps(data, ensure_ascii=False, separators=(",", ":"))
print("Pretty:\n", pretty)
print("Compact:", compact)
Pretty:
{
"a": [
3,
2,
1
],
"z": 1
}
Compact: {"z":1,"a":[3,2,1]}
文字化けを防ぐUTF-8の指定
Windowsなどでは既定のテキストエンコーディングがUTF-8でない場合があります。
常にopen(..., encoding="utf-8")
を明示し、書き出し時はensure_ascii=False
を併用しましょう。
# UTF-8で書いてUTF-8で読む(文字化け対策の基本形)
import json
data = {"title": "文字化け対策", "note": "UTF-8で統一しましょう。"}
with open("utf8.json", "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
with open("utf8.json", "r", encoding="utf-8") as f:
loaded = json.load(f)
print("OK:", loaded)
OK: {'title': '文字化け対策', 'note': 'UTF-8で統一しましょう。'}
エディタ側のファイル保存エンコーディングもUTF-8に統一しておくとより安全です。
文字列で扱う json.dumpsとjson.loads
ファイルを使わずにJSON化して確認
ちょっと中身を見たい、といった場面ではjson.dumps
で文字列化してprintするのが手軽です。
# dumps で文字列化して型と中身を確認
import json
data = [{"id": 1}, {"id": 2}]
s = json.dumps(data, indent=2)
print(type(s), "\n", s)
<class 'str'>
[
{
"id": 1
},
{
"id": 2
}
]
JSON文字列から辞書やリストに戻す
APIから受け取ったJSON文字列をjson.loads
でPythonのデータに変換します。
loadsのsはstringのsと覚えると区別しやすいです。
# JSON文字列をPythonオブジェクトへ
import json
json_text = '{"a": 1, "flags": [true, false], "nothing": null}'
obj = json.loads(json_text)
print(type(obj), obj) # dict
print(type(obj["flags"]), obj["flags"]) # list
print(type(obj["nothing"]), obj["nothing"]) # NoneType/None
<class 'dict'> {'a': 1, 'flags': [True, False], 'nothing': None}
<class 'list'> [True, False]
<class 'NoneType'> None
PythonでJSONを扱う注意点とコツ
dumpとdumps、loadとloadsの違い
dump/loadはファイル、dumps/loadsは文字列の相手です。
名前の末尾のs
が付く方が「string」と覚えてください。
json.dump(obj, fp, ...)
: objをJSONとしてファイルオブジェクトfpに書くjson.load(fp)
: ファイルオブジェクトfpからJSONを読みobjにするjson.dumps(obj, ...)
: objをJSON文字列にするjson.loads(s)
: JSON文字列sをobjにする
# 4関数の最小例で違いを確認
import json
# ファイルに書く: dump
with open("tmp.json", "w", encoding="utf-8") as f:
json.dump({"x": 1}, f)
# 文字列を作る: dumps
s = json.dumps({"x": 1})
print("dumps ->", type(s), s)
# ファイルから読む: load
with open("tmp.json", "r", encoding="utf-8") as f:
obj1 = json.load(f)
# 文字列から読む: loads
obj2 = json.loads('{"x": 1}')
print("load ->", type(obj1), obj1)
print("loads ->", type(obj2), obj2)
dumps -> <class 'str'> {"x": 1}
load -> <class 'dict'> {'x': 1}
loads -> <class 'dict'> {'x': 1}
末尾カンマやコメントはエラーになる
JSONはJavaScriptに似ていますが別仕様です。
配列やオブジェクトの末尾カンマ、コメントはエラーです。
# 末尾カンマのエラー例
import json
bad = '{"a": 1,}' # "1," の後ろのカンマが余計
try:
json.loads(bad)
except json.JSONDecodeError as e:
print("JSONDecodeError:", e)
JSONDecodeError: Expecting property name enclosed in double quotes: line 1 column 9 (char 8)
コメントに関しては前述の章の通り//
や/* */
も不可です。
JSONは「厳密に書く」ことが成功の近道です。
タプルはJSONではリストになる
JSONにタプル型はありません。
Pythonのtuple
はJSONでは配列になり、読み戻すとlist
になります。
必要ならtuple(...)
で再変換してください。
# タプルは配列として保存され、読み戻すとリストになる
import json
t = (1, 2, 3)
s = json.dumps(t) # JSON文字列へ
back = json.loads(s) # Pythonへ戻す
print(type(s), s) # str と "[1, 2, 3]"
print(type(back), back) # list と [1, 2, 3]
<class 'str'> [1, 2, 3]
<class 'list'> [1, 2, 3]
数値・bool・Noneは自動で対応する
整数・浮動小数・ブール値・Noneはそのまま保存/復元できます。
演算や比較は通常のPythonの値として扱えます。
# 数値・bool・None の往復確認
import json
data = {"i": 1, "f": 2.5, "flag": False, "nothing": None}
s = json.dumps(data)
obj = json.loads(s)
print(type(obj["i"]).__name__, obj["i"])
print(type(obj["f"]).__name__, obj["f"])
print(type(obj["flag"]).__name__, obj["flag"])
print(type(obj["nothing"]).__name__, obj["nothing"])
int 1
float 2.5
bool False
NoneType None
浮動小数点は丸め誤差の影響を受ける場合があるため、厳密性が必要な金額などでは文字列にするなどの別対策も検討してください。
まとめ
ここまででjson.dump/json.load(ファイル)とjson.dumps/json.loads(文字列)の使い分け、日本語を崩さないensure_ascii=False
とencoding="utf-8"
の指定、整形(indent
やsort_keys
)、そして末尾カンマ・コメント禁止やタプルがリストになるといった注意点を確認しました。
まずは辞書とリストで小さく始め、文字列で結果を確認しながら、問題なければファイルに保存する、という流れで練習してみてください。
JSONは「シンプルに、厳密に」がコツです。
慣れてくるとAPI連携や設定の管理がぐっと楽になります。