verilog書く人

自称ASIC設計者です。どなたかkaggle一緒に出ましょう。

【Python】jsonで自作クラスを含んだデータをシリアライズ/デシリアライズする

 オブジェクトをファイルに保存したい時、JSON便利ですよね。

パフォーマンスが欲しい時はprotobufやpickleを使いますが、そうでもない時はJSONxml

私的にはJSONが一番見やすいので、断然JSONを使います。

今日はJSONで自作クラスを保存したい時の話です。

 

 

シリアライズ(保存)

 

int、float、boolean、None、stringなどの基本型は普通にシリアライズ可能です。

 

with open('myjson.json', 'w') as f:
    json.dump({'1':1, '2': 'two', '3': 3.0, '4': None}, f) # JSONファイルにシリアライズ


しかし、このように自作クラス(下の例ではNotSettedParameter)を作り、

class NotSettedParameter(object): pass

with open('myjson.json', 'w') as f:
    json.dump({'1': 1, '2': 2, 3: NotSettedParameter()}, f)

シリアライズ対象にしたとすると、

TypeError: <__main__.NotSettedParameter object at 0x7fd1cc7567b8> is not JSON serializable

のようにエラーが出ます。
このときにはJSONエンコーダーを自分で作成してNotSettedParameter型の変数をどのようにエンコードするか定義する必要があります。
 

例えば、NotSettedParameterは'NotSettedParameter'という文字列にエンコードするとします。

そのためには、エンコーダー

import json
class MyJSONEncoder(json.JSONEncoder):
    def default(self, o):
        if isinstance(o, NotSettedParameter): # NotSettedParameterは'NotSettedParameter'としてエンコード
            return 'NotSettedParameter'
        return super(MyJSONEncoder, self).default(o) # 他の型はdefaultのエンコード方式を使用

のように定義すればよいです。エンコーダはシリアライズ時にcls引数として設定します。

with open('a.json', 'w') as f:
    json.dump({'1':1, '2': 2, 3: NotSettedParameter()}, f, cls=MyJSONEncoder)

 

シリアライズ(読み込み)


シリアライズしたJSONを読み込みたいとします。

このとき、

with open('myjson.json', 'r') as f:
    A = json.load(f)

とすると、

{'1': 1, '3': 'NotSettedParameter', '2': 2}

という結果になります。これは'NotSettedParameter'はそのまま文字列の'NotSettedParameter'に解釈されていることを意味します。
そうではなく、自作クラスのNotSettedParameter()としてデシリアライズしてほしいときは、デコーダーを自分で定義する必要があります。今回の場合、デコーダは、

def myhook(dict):
    for key, value in dict.items():
        if value == 'NotSettedParameter':  # 'NotSettedParameter'はNotSettedParameterとしてデコード
            dict[key] = NotSettedParameter()
    return dict # 他の型はdefaultのデコード方式を使用

シリアライズ時にobject_hook関数として指定します。

with open('myjson.json', 'r') as f:
    A = json.load(f, object_hook=myhook)

 

参考:

pythonでjson出力する際で対応していない型(e.g. datetime)の値を変換しながら出力したい - Qiita