I would like to define a subclass of dict with, in particular, custom JSON serialization. The problem I'm running into is that if I directly subclass dict, then the json module does not enter into the 'default' function, when encountering instances of my dict subclass, there by bypassing my custom serialization. Consider:
import collections
class MyDictA(dict):
# subclass of dict
def to_json(self):
return {
"items": dict(self),
"_type": self.__module__ + "." + self.__class__.__name__,
}
def __repr__(self):
return self.__class__.__name__ + "(" + repr(dict(self)) + ")"
class MyDictB(collections.MutableMapping):
# behaves like a dict, is not a dict
# can easily implement the remaining dict methods
def __getitem__(self, item):
return self.__dict__[item]
def __setitem__(self, key, value):
self.__dict__[key] = value
def to_json(self):
return {
"items": vars(self),
"_type": self.__module__ + "." + self.__class__.__name__,
}
def __repr__(self):
return self.__class__.__name__ + "(" + repr(self.__dict__) + ")"
def __iter__(self):
return self.__dict__.__iter__()
def __len__(self):
return len(self.__dict__)
def __delitem__(self, key):
del self.__dict__[key]
I can easily implement the remaining dict methods, so that MyDictB is a drop-in replacement for dict, but this somehow feels non-pythonic.
Now we implement the custom serialization:
import json
def my_default(obj):
if hasattr(obj, "to_json"):
return obj.to_json()
else:
return obj
Example:
A = MyDictA()
A["foo"] = "bar"
B = MyDictB()
B["foo"] = "bar"
Result:
>>> print(A)
MyDictA({'foo': 'bar'})
>>> print(B)
MyDictB({'foo': 'bar'})
>>> print(jsonA)
{"foo": "bar"}
>>> print(jsonB)
{"_type": "__main__.MyDictB", "items": {"foo": "bar"}}
As you can see, only MyDictB passes through the custom serialization, 'my_default'; instances of MyDictA never do, since they are dict instances.
the problem in the json module is that it conditions on isinstance(obj, dict), see the implementation of "_iterencode" in json/encoder.py.
Note:
>>> isinstance(A, collections.Mapping)
True
>>> isinstance(B, collections.Mapping)
True
>>> isinstance(A, dict)
True
>>> isinstance(B, dict)
False
Is there a better way to get the json module to respect my subclassing of dict?