I published a lib on PyPi that does that, which pushed to write a complete readme, that I will reproduce here if anybody is interested in more discussion about this, along with my conclusions:
Overall, it seems like the cost of maintenance is going to be insignificant.
While the value is reduced with objects that can't be patched during runtime such as datetime (TypeError: can't set attributes of built-in/extension type 'datetime.datetime') that you'd need to import from that library instead of from datetime, it still brings value in making serialization and deserialization into the versatile and popular JSON format easier and more reusable than with the current object_hook, by leveraging typical object oriented programing in a very boring way that makes it easy for anyone to grasp.
The README looks like:
Instead of:
from json import loads, dumps
from uuid import UUID, uuid4
obj = uuid4()
encoded = dumps(str(obj))
decoded = UUID(loads(encoded))
assert obj == decoded
We can do:
from jsonlight import loads, dumps
from uuid import UUID, uuid4
obj = uuid4()
encoded = dumps(obj)
decoded = loads(UUID, encoded)
assert obj == decoded
This is because jsonlight patches uuid.UUID class to add the following methods:
- ``__jsondump__``: return a representation of self with JSON data types
- ``__jsonload__``: instantiate an object based on the result from __jsondump__
You can see that the main difference with ``json.loads`` is that
``jsonlight.loads`` requires a type as the first argument. This is because
``jsonlight.loads`` will first call ``json.loads`` to convert the string into a
Python object with basic JSON types, and then pass that to the type's
``__jsonload__`` function.
Other types can't be monkey patched, so you have to import them from jsonlight
instead, which is the sad case of datetime:
from jsonlight import loads, dumps, datetime
obj = datetime.now()
assert obj == loads(datetime, dumps(obj))
You may also define ``__jsondump__`` and ``__jsonload__`` methods on your own
classes, example:
from jsonlight import load
class YourClass:
def __init__(self, uuid=None):
self.uuid = uuid or uuid4()
def __jsondump__(self):
return dict(uuid=self.uuid)
@classmethod
def __jsonload__(cls, data):
return cls(load(UUID, data['uuid'])
# This also works, but would not illustrate how to support recursion
# return cls(UUID(data['uuid']))
As you can see:
- you don't have to worry about calling ``__jsondump__`` on return values of
your own ``__jsondump__`` because ``jsonlight.dumps`` will do that
recursively,
- you have full control on deserialization just like with ``__setstate__``, but if
you call jsonlight.load in there yourself then you don't have to duplicate
deserialization logic or bother calling ``__jsonload__`` on nested objects
yourself,
Monkey-patched stdlib objects are:
- UUID
- Path
Feel free to add more.
Stdlib objects that couldn't be monkey patched, and that you have to import
from jsonlight instead are:
- datetime