JSON Cerealizer
A simple library for monkey patching the python json library, thereby making it easier to add serializers for objects that cannot be encoded using the default JSONEncoder.
Install
pip install json-cerealizer
TL;DR Example
Import the library, run the monkey patch, then register functions to handle class serialization. Call json.dumps as usual and receive output for your typically un-encodable objects.
>>> from datetime import datetime
>>> import json
>>> import json_cerealizer
>>>
>>> json_cerealizer.patch()
>>> json_cerealizer.add_serializer(datetime, datetime.isoformat)
>>>
>>> time_dict = {"now": datetime.now()}
>>> json.dumps(time_dict)
'{"now": "2017-08-21T19:57:31.761091"}'
Use Case
The JSON standard can only represent a handful of data types. If you attempt to serialize a non-standard type, you receive a TypeError. The following is an example showing that datetime objects cannot be serialized by the default JSON encoder.
>>> import json
>>> from datetime import datetime
>>>
>>> time_dict = {"now": datetime.now()}
>>> time_dict
{'now': datetime.datetime(2017, 8, 21, 19, 47, 17, 785813)}
>>> json.dumps(time_dict)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3.5/json/__init__.py", line 230, in dumps
return _default_encoder.encode(obj)
File "/usr/lib/python3.5/json/encoder.py", line 198, in encode
chunks = self.iterencode(o, _one_shot=True)
File "/usr/lib/python3.5/json/encoder.py", line 256, in iterencode
return _iterencode(o, 0)
File "/usr/lib/python3.5/json/encoder.py", line 179, in default
raise TypeError(repr(o) + " is not JSON serializable")
TypeError: datetime.datetime(2017, 8, 21, 19, 47, 17, 785813) is not JSON serializable
This issue is easily resolved by subclassing the json.JSONEncoder class, allowing you to specify how objects should be serialized.
>>> import json
>>> from datetime import datetime
>>>
>>> class MyEncoder(json.JSONEncoder):
... def default(self, obj):
... if isinstance(obj, datetime):
... return obj.isoformat()
...
>>> time_dict = {"now": datetime.now()}
>>> json.dumps(time_dict, cls=MyEncoder)
'{"now": "2017-08-21T19:57:31.761091"}'
While this works fine in small cases, it becomes bloated when you want to add
support for several more types. This is where json-cerealizer shines. Here we
monkey patch the json library, then register a function to handle instances of
a particular class. In this case, we are saying instances of datetime
should
be passed into the function datetime.isoformat
, which will return a value
that is natively serializable.
>>> from datetime import datetime
>>> import json
>>> import json_cerealizer
>>>
>>> json_cerealizer.patch()
>>> json_cerealizer.add_serializer(datetime, datetime.isoformat)
>>>
>>> time_dict = {"now": datetime.now()}
>>> json.dumps(time_dict)
'{"now": "2017-08-21T19:57:31.761091"}'
To solidify our understanding, lets handle a class of our own.
>>> import json
>>> import json_cerealizer
>>>
>>> class Rectangle(object):
... def __init__(self, x, y):
... self.x = x
... self.y = y
...
>>> def rectangle_to_dict(rec):
... return {"x": rec.x, "y": rec.y, "area": rec.x * rec.y}
...
>>> json_cerealizer.patch()
>>> json_cerealizer.add_serializer(Rectangle, rectangle_to_dict)
>>> r = Rectangle(3, 4)
>>> json.dumps(r)
'{"x": 3, "y": 4, "area": 12}'