[解決!Python]JSON形式のファイルや文字列を読み書きするには解決!Python

jsonモジュールが提供するload/dump関数でJSONファイルの読み書きを行ったり、loads/dumps関数でJSON形式のデータを含む文字列のエンコード/デコードをしたりする方法を紹介する。

» 2023年10月04日 05時00分 公開
[かわさきしんじDeep Insider編集部]
「解決!Python」のインデックス

連載目次

* 本稿は2022年08月30日に公開された記事をPython 3.12.0で動作確認したものです(確認日:2023年10月04日)。


{
    "name": "deep insider",
    "url": "https://atmarkit.itmedia.co.jp/ait/subtop/di/",
    "staffs": [
        {"name": "一色", "age": 40},
        {"name": "かわさき", "age": 60}
    ]
}


今回使用するサンプルのJSONファイル(di.jsonファイル)

import json

# json.load関数を使ったjsonファイルの読み込み
with open('di.json') as f:
    di = json.load(f)

print(di['name'])  # deep insider:キーを指定して値を取得

for k, v in di.items():  # キー/値の組を列挙
    print(f'{k}: {v}')
# 出力結果:
#name: deep insider
#url: https://atmarkit.itmedia.co.jp/ait/subtop/di/
#staffs: [{'name': '一色', 'age': 40}, {'name': 'かわさき', 'age': 60}]

# json.dump関数を使ったJSONファイルへの書き出し
with open('tmp.json', 'wt') as f:
    json.dump(di, f)

# 書き出した内容を確認
with open('tmp.json') as f:
    print(f.read())
# 出力結果:
#{"name": "deep insider", "url": "https://atmarkit.itmedia.co.jp/ait/subtop/di/
#", "staffs": [{"name": "\u4e00\u8272", "age": 40}, {"name": "\u304b\u308f\u3055
#\u304d", "age": 60}]}

# 非ASCII文字を\uXXXX形式にエスケープしない
with open('tmp.json', 'wt') as f:
    json.dump(di, f, ensure_ascii=False)

with open('tmp.json') as f:
    print(f.read())
# 出力結果:
#{"name": "deep insider", "url": "https://atmarkit.itmedia.co.jp/ait/subtop/di/
#", "staffs": [{"name": "一色", "age": 40}, {"name": "かわさき", "age": 60}]}

# 整形して(インデント付きで)ファイルへ書き出し
with open('tmp.json', 'wt') as f:
    json.dump(di, f, indent=2, ensure_ascii=False)

with open('tmp.json') as f:
    print(f.read())
# 出力結果:
#{
#  "name": "deep insider",
#  "url": "https://atmarkit.itmedia.co.jp/ait/subtop/di/",
#  "staffs": [
#    {
#      "name": "一色",
#      "age": 40
#    },
#    {
#      "name": "かわさき",
#      "age": 60
#    }
#  ]
#}

# json.dumps関数を使ったJSON形式の文字列へのエンコード
json_str = json.dumps(di)
print(json_str)
#{"name": "deep insider", "url": "https://atmarkit.itmedia.co.jp/ait/subtop/di/
#", "staffs": [{"name": "\u4e00\u8272", "age": 40}, {"name": "\u304b\u308f\u3055
#\u304d", "age": 60}]}

json_str = json.dumps(di, indent=2)
print(json_str)
# 出力結果:
#{
#  "name": "deep insider",
#  "url": "https://atmarkit.itmedia.co.jp/ait/subtop/di/",
#  "staffs": [
#    {
#      "name": "\u4e00\u8272",
#      "age": 40
#    },
#    {
#      "name": "\u304b\u308f\u3055\u304d",
#      "age": 60
#    }
#  ]
#}

# json.loads関数を使ったJSON形式の文字列のデコード
json_data = json.loads(json_str)
print(json_data)
#{'name': 'deep insider', 'url': 'https://atmarkit.itmedia.co.jp/ait/subtop/di/
#', 'staffs': [{'name': '一色', 'age': 40}, {'name': 'かわさき', 'age': 60}]}


JSONファイルの読み込みと書き出し

 PythonでJSON形式のファイルを読み書きするには、jsonモジュールのload関数(読み込み)とdump関数(書き出し)を使用する。

JSONファイルの読み込み

 以下にJSONファイルの読み込みに使用するjson.load関数の構文を示す。

json.load(fp, *, ……)


 必須の引数は、読み込み対象のファイルを参照するファイルオブジェクト(fp)だけで、それ以外に幾つかのキーワード専用引数が用意されている(詳細はPythonのドキュメント「json.load」などを参照されたい)。

名前 説明
parse_float JSONデータに埋め込まれた浮動小数点数をパースするのに使用する関数。デフォルトはfloat関数呼び出しと等価な処理が行われる(文字列から浮動小数点数に変換される)
parse_int JSONデータに埋め込まれた整数をパースするのに使用する関数。デフォルトはint関数呼び出しと等価な処理が行われる(文字列から整数に変換される)
parse_constant JSONデータに文字列「'-Infinity'」「'Infinity'」「'NaN'」が含まれていた際に呼び出される関数。デフォルトではこれらはfloat型の値「-Inf」「Inf」「nan」に変換される。
object_hook JSONデータを構成するデータ片の変換後に何らかの処理を行う必要がある場合に呼び出す関数。データ片が構造を持つ場合、それらは辞書としてこの関数に渡される
object_pairs_hook JSONデータを構成するデータ片の変換後に何らかの処理を行う必要がある場合に呼び出す関数。データ片が構造を持つ場合、それらはキーと値を要素とするタプルのリストとしてこの関数に渡される
json.load関数のキーワード専用引数(抜粋)

 ここでは以下に示すdi.jsonファイルを例に話をしていこう。

{
    "name": "deep insider",
    "url": "https://atmarkit.itmedia.co.jp/ait/subtop/di/",
    "staffs": [
        {"name": "一色", "age": 40},
        {"name": "かわさき", "age": 60}
    ]
}



 以下にJSONファイルを読み込むシンプルな例を示す。

import json

with open('di.json') as f:
    di = json.load(f)

print(di['name'])  # deep insider:キーを指定して値を取得


 単純にdi.jsonファイルを読み込み用にオープンし、json.load関数にそれを渡すだけで、その内容が返送される。多くの場合、JSONファイルは上に示したdi.jsonファイルのような構造を持っており、そうであればjson.load関数の戻り値は辞書となる。そのため、最後の行に示したようにキーを指定することで、その値を取得できる。

 辞書を反復して次のような処理も行える。

for k, v in di.items():  # キー/値の組を列挙
    print(f'{k}: {v}')
# 出力結果:
#name: deep insider
#url: https://atmarkit.itmedia.co.jp/ait/subtop/di/
#staffs: [{'name': '一色', 'age': 40}, {'name': 'かわさき', 'age': 60}]


 上に示したキーワード引数の使用例として、parse_intにfloat関数を指定する例を以下に示す。

with open('di.json') as f:
    di = json.load(f, parse_int=float)

print(di['staffs'][0])  # {'name': '一色', 'age': 40.0}


 整数値のパースにfloat関数を指定したので、'age'キーの値が40.0と浮動小数点数値になった。

JSONファイルへの書き出し

 JSONファイルへの書き出しにはjson.dump関数を使用する。

json.dump(obj, fp, *, ……)


 必須の引数はJSON形式に変換する対象であるobjと、書き出し先のファイルオブジェクトfpだ(実際にはio.StringIOクラスのインスタンスなど、ファイルオブジェクトのように使えるものであれば書き出し先として使用できる)。他のキーワード専用引数を幾つか以下に紹介する。詳しくはPythonのドキュメント「json.dump」などを参照されたい。

キーワード専用引数 説明
ensure_ascii JSON形式のデータに変換する際に非ASCII文字を「\uXXXX」形式の文字にエスケープするかどうかの指定。デフォルト値はTrue(エスケープする)
check_circular objに循環参照があるかのチェックをするかどうかの指定。デフォルト値はTrue(チェックを行う)。Falseを指定した場合、チェックは行われなくなるが深刻な結果となるかもしれない
indent 書き出したJSONファイルに付けるインデントの指定。非負の整数値を指定するとその値をインデントレベルとして見やすく整形される。'\t'などの文字列を指定すると、その文字を使ってインデントが付けられる。デフォルト値はNoneで整形はされない
json.dump関数のキーワード専用引数(抜粋)

 先ほどdi.jsonファイルから読み込んで辞書に格納されたデータを、tmp.jsonファイルに書き出すコード例を以下に示す。

with open('tmp.json', 'wt') as f:
    json.dump(di, f)

# 書き出した内容を確認
with open('tmp.json') as f:
    print(f.read())
# 出力結果:
#{"name": "deep insider", "url": "https://atmarkit.itmedia.co.jp/ait/subtop/di/
#", "staffs": [{"name": "\u4e00\u8272", "age": 40}, {"name": "\u304b\u308f\u3055
#\u304d", "age": 60}]}


 「一色」や「かわさき」という非ASCII文字が「\uXXXX」形式にエスケープされている点と、インデントなどを使って見やすくなるように整形されていない点に注目しよう。

 「\uXXXX」形式にエスケープするのをやめるには、先に示したensure_asciiキーワード引数にFalseを指定する。以下に例を示す。

with open('tmp.json', 'wt') as f:
    json.dump(di, f, ensure_ascii=False)

with open('tmp.json') as f:
    print(f.read())
# 出力結果:
#{"name": "deep insider", "url": "https://atmarkit.itmedia.co.jp/ait/subtop/di/
#", "staffs": [{"name": "一色", "age": 40}, {"name": "かわさき", "age": 60}]}


 この例では、ensure_asciiキーワード専用引数にfalseを指定して書き出しを行い、その後、tmp.jsonファイルの内容を表示している。「一色」と「かわさき」がエスケープされていない点に注目しよう。

 タブ幅2でインデントを付ける例を以下に示す。

with open('tmp.json', 'wt') as f:
    json.dump(di, f, indent=2, ensure_ascii=False)

with open('tmp.json') as f:
    print(f.read())
# 出力結果:
#{
#  "name": "deep insider",
#  "url": "https://atmarkit.itmedia.co.jp/ait/subtop/di/",
#  "staffs": [
#    {
#      "name": "一色",
#      "age": 40
#    },
#    {
#      "name": "かわさき",
#      "age": 60
#    }
#  ]
#}


 この例ではタブ幅を2に指定したので、それに従って空白文字と改行が挿入されている。また、ensure_asciiキーワード専用引数をfalseに指定しているので、上の例と同様にエスケープがされていない。

JSON形式の文字列のデコード/エンコード

 json.load関数とjson.dump関数はファイルオブジェクトを対象としたものだったが、文字列を対象にJSONデータのデコード/エンコードを行うことも可能だ。これにはjson.loads関数とjson.dumps関数を使用する。関数名の最後は恐らく「str」の「s」だろう。

json.loads(s, *, ……)
json.dumps(obj, *, ……)


 json.loads関数はファイルオブジェクトの代わりにJSON形式のデータを含んだ文字列sを第1引数に指定するところが、json.dumps関数はファイルオブジェクトを指定しないところが上で見た2つの関数との違いだ(json.dumps関数はJSON形式にエンコードした結果が戻り値となる)。なお、キーワード引数はjson.load関数/json.dump関数と同様である。

 以下に使用例を示す(文字列を対象とすること以外は上と同様なので、説明は省略する)。

# json.dumps関数を使ったJSON形式の文字列へのエンコード
json_str = json.dumps(di)
print(json_str)
#{"name": "deep insider", "url": "https://atmarkit.itmedia.co.jp/ait/subtop/di/
#", "staffs": [{"name": "\u4e00\u8272", "age": 40}, {"name": "\u304b\u308f\u3055
#\u304d", "age": 60}]}

json_str = json.dumps(di, indent=2)
print(json_str)
# 出力結果:
#{
#  "name": "deep insider",
#  "url": "https://atmarkit.itmedia.co.jp/ait/subtop/di/",
#  "staffs": [
#    {
#      "name": "\u4e00\u8272",
#      "age": 40
#    },
#    {
#      "name": "\u304b\u308f\u3055\u304d",
#      "age": 60
#    }
#  ]
#}

# json.loads関数を使ったJSON形式の文字列のデコード
json_data = json.loads(json_str)
print(json_data)
#{'name': 'deep insider', 'url': 'https://atmarkit.itmedia.co.jp/ait/subtop/di/
#', 'staffs': [{'name': '一色', 'age': 40}, {'name': 'かわさき', 'age': 60}]}


「解決!Python」のインデックス

解決!Python

Copyright© Digital Advantage Corp. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。