はじめに
JSONって便利ですよね
でもちょっと扱いを間違えると場合によっては上書きでデータが飛んでしまったり...
本題
問題です。
data = {2024: "令和6年"}
というdict型のデータがあったとします。
ここで、
print(data[2024])
を実行したら何が表示されるでしょうか?
...簡単ですね。
令和6年
です。
JSON化して保存する
データが出来上がったので保存します。
import json
data = {2024: "令和6年"}
with open("data.json", mode="w") as f:
f.write(json.dumps(data))
保存したJSONを利用する
出来上がったデータを読み込んで西暦2024年が和暦何年なのかを参照します。
with open("data.json") as f:
data = json.loads(f.read())
print("西暦2024年は、和暦"+data[2024]+"です。") # KeyError: 2024
エラーが発生してしまいました。
どうして?
元のデータと比較してみます。
import json
data = {2024: "令和6年"}
print(data) # {2024: '令和6年'}
data = json.loads(json.dumps(data))
print(data) # {'2024': '令和6年'}
見て分かる通り、一度JSONにするとint型のkeyは自動的にstr型に変換されてしまいます。
もしも存在しない場合に自動生成される仕様だと...
試しに簡易的なユーザー情報で試してみます。
import json
default_userdata = {"name": "ゲストユーザー"}
users = {1: {"name": "テストユーザー"}}
user_id = 1
# 模擬的な保存 & ロード処理
users = json.loads(json.dumps(users))
if not user_id in users:
users[user_id] = default_userdata
print(users[user_id]) # デフォルトで上書きされ、 {'name': 'ゲストユーザー'} が出力される
上書きされてしまいました。
じゃあどうするのか
当たり前ですが、JSON以外の方法で保存するか、keyをstrとして扱えばこの問題は防げます。
import json
default_userdata = {"name": "ゲストユーザー"}
users = {"1": {"name": "テストユーザー"}}
user_id = "1"
# 模擬的な保存 & ロード処理
users = json.loads(json.dumps(users))
if not user_id in users:
users[user_id] = default_userdata
print(users[user_id]) # 正常に {'name': 'テストユーザー'} が出力される
さいごに
かなりやりがちなことですが、この仕様はあまり知られていません。
既に運用しているシステムに跡付けする形でこんな感じにプログラムするとデータが飛んでしまいます。
バックアップがあれば復元できますが、無ければ取り返しがつかないことになってしまいます。
短い記事でしたが、これでJSONのkeyにintを使うことの危険性が伝わったと思います。
今後の開発で参考になればと思います。