Custom JSON Encoder with python

The json
package can encode python objects as strings with the json.JSONEncoder
class, and decodes strings into python objects using the json.JSONDecoder
class.
Here is an example :
import json
# Convert a dict to a string
json.dumps({"name": "Paul", "age": 24})
# Convert a string to a dict
json.loads('{"name": "Paul", "age": 24}')
By default, these class supports dict, list, tuple, str, int, float, bool and None values. But it’s possible to overload JsonEncoder and JsonDecoder to support Datetime, Decimal or any of your classes.
It can be useful when you want to serialize or deserialize objects. I like to use it when I store mongodb queries (which are json objects) as a string in database.
Let’s start with json encoding.
# extend the json.JSONEncoder class
class JSONEncoder(json.JSONEncoder):
# overload method default
def default(self, obj):
# Match all the types you want to handle in your converter
if isinstance(obj, datetime):
return arrow.get(obj).isoformat() # Call the default method for other types
return json.JSONEncoder.default(self, obj)
In the above example, we convert datetime objects to isoformat string.
We can also convert Decimal or any other object
elif isinstance(obj, Decimal):
return float(Decimal(obj).quantize(Decimal("0.01"), ROUND_UP))
elif isinstance(obj, MyClass):
return JSONEncoder().encode({
"value1": obj.value_1,
"value2": obj.value_2
})
And let’s do the opposite with JSONDecoder. It’s a little bit different here, we have to pass a method to object_hook in the constructor. We will implement our custom deserializations conditions in this method.
class JSONDecoder(json.JSONDecoder):
def __init__(self, *args, **kwargs):
json.JSONDecoder.__init__(self, object_hook=self.object_hook, *args, **kwargs)
def object_hook(self, obj):
# handle your custom classes
if isinstance(obj, dict):
if "value1" in obj and "value2" in obj:
print(obj)
return MyClass(obj.get("value_1"), obj.get("value_2"))
# handling the resolution of nested objects
if isinstance(obj, dict):
for key in list(obj):
obj[key] = self.object_hook(obj[key])
return obj
if isinstance(obj, list):
for i in range(0, len(obj)):
obj[i] = self.object_hook(obj[i])
return obj
# resolving simple strings objects
# dates
if isinstance(obj, str):
obj = self._extract_date(obj)
return obj
We can add two shortcuts for a more readable code.
def json_encode(data):
return JSONEncoder().encode(data)
def json_decode(string):
return JSONDecoder().decode(string)
And use it this way
from JsonEncoder import json_encode, json_decodemy_obj = MyClass("value1", "value2")
json_encode(my_obj)my_str = '{"name": "Paul", "birthdate": "1992-02-22T00:00:00+00:00", "int_number": 122}'
json_decode(my_str)
You can find all the code, with examples on Github : JsonEncoder.
If you want like to read more about the json
package, you can refer to the documentation.