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