>>> import json
>>> json.dumps(42)
'42'
>>> type(_)
<class 'str'>simple, things should be simple >>> import json
>>> json.dumps({
... 'name': 'H',
... 'mass': {'value': 1.00784, 'units': 'Da'},
... 'atomic number': 1,
... 'constituents': ['proton', 'electron']
... })
...
'{"name": "H", "mass": {"value": 1.00784, "units": "Da"}, "atomic number": 1, "constituents": ["proton", "electron"]}'
>>> type(_)
<class 'str'>complex things should be possible >>> import uuid
>>> the_id = uuid.uuid4()
>>> the_id
UUID('af281d69-6cca-49f5-b119-abccd46bb32c')
>>> json.dumps({'the_id': the_id})
Traceback (most recent call last):
...
TypeError: Object of type UUID is not JSON serializable
when serializing dict item 'the_id'errors should never pass silently >>> import dataclasses
>>> @dataclasses.dataclass
... class Atom:
... name: str
... mass: tuple
... atomic_number: int
... constituents: list[str]
...
>>> hydrogen = Atom('H', (1.00784, 'Da'), 1, ['proton', 'electron'])
>>> hydrogen
Atom(name='H', mass=(1.00784, 'Da'), atomic_number=1, constituents=['proton', 'electron'])
>>> json.dumps(hydrogen)
Traceback (most recent call last):
...
TypeError: Object of type Atom is not JSON serializableerrors should never pass silently (2) >>> help(json.dumps)
Help on function dumps in module json:
dumps(
obj,
*,
skipkeys=False,
ensure_ascii=True,
check_circular=True,
allow_nan=True,
cls=None,
indent=None,
separators=None,
default=None,
sort_keys=False,
**kw
)
...
``default(obj)`` is a function that should return a serializable version
of obj or raise TypeError. The default simply raises TypeError.
...
>>> print(chr(0x1F914))
π€
>>> def nondefault(obj):
... print(f'{type(obj)}')
... return 42
...
>>> json.dumps({'the_id': the_id}, default=nondefault)
<class 'uuid.UUID'>
'{"the_id": 42}'
>>> json.dumps(hydrogen, default=nondefault)
<class '__main__.Atom'>
'42'unless explicitly silenced >>> print(chr(0x1F4A1))
π‘
>>> def nondefault(obj):
... if isinstance(obj, uuid.UUID):
... return str(obj)
... elif isinstance(obj, Atom):
... return dataclasses.asdict(obj)
... else:
... return obj
...
>>> json.dumps({'the_id': the_id}, default=nondefault)
'{"the_id": "af281d69-6cca-49f5-b119-abccd46bb32c"}'
>>> json.dumps(hydrogen, default=nondefault)
'{"name": "H", "mass": [1.00784, "Da"], "atomic_number": 1, "constituents": ["proton", "electron"]}'unless explicitly silenced (2) >>> from functools import singledispatch
>>> help(singledispatch)
Help on function singledispatch in module functools:
singledispatch(func)
Single-dispatch generic function decorator.
Transforms a function into a generic function, which can have different
behaviours depending upon the type of its first argument. The decorated
function acts as the default implementation, and additional
implementations can be registered using the register() attribute of the
generic function.
>>> print(chr(0x1F4A1))
π‘
>>> import typing
>>> @singledispatch
... def to_dict(obj: typing.Any) -> dict:
... raise NotImplementedError("Unsupported type: {obj_type}".format(obj_type=type(obj)))
...
>>> print(chr(0x1F937))
π€·if the implementation is hard to explain, it's a bad idea >>> to_dict(the_id)
Traceback (most recent call last):
...
NotImplementedError: Unsupported type: <class 'uuid.UUID'>
>>> to_dict(hydrogen)
Traceback (most recent call last):
...
NotImplementedError: Unsupported type: <class '__main__.Atom'>
>>> print(chr(0x1F914))
π€
>>> @to_dict.register
... def _(obj: uuid.UUID) -> dict:
... return {'__type__': 'uuid.UUID', 'value': str(obj)}
...
>>> @to_dict.register
... def _(obj: Atom) -> dict:
... return dataclasses.asdict(obj)
...
>>> to_dict(the_id)
{'__type__': 'uuid.UUID', 'value': 'f2078f61-4e9d-4af8-ace0-2f03961fa59d'}
>>> to_dict(hydrogen)
{"name": "H", "mass": [1.00784, "Da"], "atomic_number": 1, "constituents": ["proton", "electron"]}
>>> print(chr(0x1F604))
πif the implementation is easy to explain, it may be a good idea >>> print(chr(0x1F914), chr(0x1F4A1))
π€ π‘
>>> def dumps(obj: typing.Any, default=to_dict, **kwargs) -> str:
... return json.dumps(obj, default=default, **kwargs)
...
>>> dumps({'the_id': the_id})
'{"the_id": {"__type__": "uuid.UUID", "value": "af281d69-6cca-49f5-b119-abccd46bb32c"}}'
>>> dumps(hydrogen)
'{"name": "H", "mass": [1.00784, "Da"], "atomic_number": 1, "constituents": ["proton", "electron"]}'
>>> print(dumps({'the_id': the_id}, indent=2))
{
"the_id": {
"__type__": "uuid.UUID",
"value": "af281d69-6cca-49f5-b119-abccd46bb32c"
}
}
>>> print(dumps(hydrogen, indent=2))
{
"name": "H",
"mass": [
1.00784,
"Da"
],
"atomic_number": 1,
"constituents": [
"proton",
"electron"
]
}
>>> print(chr(0x1F44C))
πthere should be one-- and preferably only one --obvious way to do it