singledispatchとjson.dumpが相性良いかも?
jsonのencodeエラーについては昔に書いたこの辺を見てもらうとして。
今回の主題は、json.dumpsに渡すdefaultの関数としてfunctools.singledispatchが有用かもという話。
使いかた
singledispatchを利用したモジュールを定義
例えば以下のようなextjsonモジュールを定義してあげる。
extjson.py
import json from functools import ( singledispatch, partial, ) @singledispatch def encode(o): raise TypeError("Object of type '%s' is not JSON serializable" % o.__class__.__name__) register = encode.register dump = partial(json.dump, indent=2, ensure_ascii=False, sort_keys=True, default=encode) dumps = partial(json.dumps, indent=2, ensure_ascii=False, sort_keys=True, default=encode) load = json.load loads = json.loads
extjsonのdump,dumpsには、defaultに定義したencode()を渡してあげる。このencode()はsingledispatchを利用したもの。
普通に使う
特に何も設定していなくても普通のjsonモジュールと同じように使える。
import extjson d = { "name": "foo", "age": 20 } print(extjson.dumps(d)) # { # "age": 20, # "name": "foo" # }
通常ならTypeErrorが出るものに対して使う
通常なら意図しなかった型の値が渡された場合にはTypeErrorになる。このような型に対する対応をsingledispatch.registerで良い感じに使う側でできる。 たとえば、datetimeオブジェクトなどは対応していない型。
import datetime as dt import extjson @extjson.register(dt.datetime) def encode_datetime(o): return o.isoformat() d = { "name": "foo", "birth": dt.datetime.now(), } print(extjson.dumps(d)) # { # "birth": "2017-09-24T05:28:16.413651", # "name": "foo" # }
悪くないような気がする。
追記:singledispatchはabcにも対応している
singledispatchはabcにも対応しているので以下の様な形で仮想継承を経由しても使える。
date,datetimeを自分で定義したStringerというクラスに所属させてみた。
import datetime as dt import extjson import abc class Stringer(abc.ABC): pass Stringer.register(dt.datetime) Stringer.register(dt.date) @extjson.register(Stringer) def encode_stringer(o): return str(o) d = { "name": "foo", "birth": dt.datetime.now(), "birthday": dt.date.today(), } print(extjson.dumps(d)) # { # "birth": "2017-09-24 12:20:27.933120", # "birthday": "2017-09-24", # "name": "foo" # }