How-to Guide

Better error messages

Better error messages for other JSON libraries

>>> import json, jsonyx
>>> try:
...     json.loads("[,]")
... except json.JSONDecodeError as exc:
...     raise jsonyx.JSONSyntaxError(exc.msg, "<string>", exc.doc, exc.pos) from None
...
Traceback (most recent call last):
  File "<string>", line 1, column 2
    [,]
     ^
jsonyx.JSONSyntaxError: Expecting value

Better error messages for encoding strings

>>> import jsonyx as json
>>> try:
...     "café".encode("ascii")
... except UnicodeEncodeError as exc:
...     raise json.TruncatedSyntaxError(
...         f"(unicode error) {exc}", "<string>", exc.object, exc.start, exc.end,
...     ) from None
...
Traceback (most recent call last):
  File "<string>", line 1, column 4-5
    café
       ^
jsonyx.TruncatedSyntaxError: (unicode error) 'ascii' codec can't encode character '\xe9' in position 3: ordinal not in range(128)

Better error messages for decoding bytes

>>> import jsonyx as json
>>> try:
...     b"caf\xe9".decode("ascii")
... except UnicodeDecodeError as exc:
...     doc = exc.object.decode(exc.encoding, "replace")
...     start = exc.object[:exc.start].decode(exc.encoding, "replace")
...     end = exc.object[:exc.end].decode(exc.encoding, "replace")
...     raise json.TruncatedSyntaxError(
...         f"(unicode error) {exc}", "<string>", doc, len(start), len(end),
...     ) from None
...
Traceback (most recent call last):
  File "<string>", line 1, column 4-5
    caf�
       ^
jsonyx.TruncatedSyntaxError: (unicode error) 'ascii' codec can't decode byte 0xe9 in position 3: ordinal not in range(128)

See also

jsonyx.format_syntax_error() for formatting the exception.

Encoding custom objects

Encoding protocol-based objects

Added in version 2.0.

Type

Required methods

"array"

__iter__()

"bool"

__bool__(), __len__() or absent for true

"float"

__str__() or __repr__()

"int"

__str__() or __repr__()

"object"

values() and items()

"str"

__str__() or __repr__()

Example with numpy:

>>> import jsonyx as json
>>> import numpy as np
>>> obj = np.array([
...     np.bool_(), np.int8(), np.uint8(), np.int16(), np.uint16(), np.int32(),
...     np.uint32(), np.intp(), np.uintp(), np.int64(), np.uint64(), np.float16(),
...     np.float32(), np.float64()
... ], dtype="O")
>>> types = {
...     "array": np.ndarray,
...     "bool": np.bool_,
...     "float": np.floating,
...     "int": np.integer
... }
>>> json.dump(obj, types=types)
[false, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.0, 0.0, 0.0, 0.0]

Note

Custom types must be registered manually, jsonyx does not infer serializability based on method presence.

Warning

Avoid specifying ABCs for types, that is very slow.

Encoding arbitrary objects

Added in version 2.1.

>>> import jsonyx as json
>>> def complex_hook(obj):
...     if isinstance(obj, complex):
...         return {"__complex__": True, "real": obj.real, "imag": obj.imag}
...     return obj
...
>>> json.dump(1 + 2j, hook=complex_hook)
{"__complex__": true, "real": 1.0, "imag": 2.0}

Tip

You can use functools.singledispatch() to make this extensible.

Warning

This function is called for every object during encoding, even if the object is normally serializable.

See also

The pickle and shelve modules which are better suited for this.

Decoding custom objects

Decoding objects using hooks

Added in version 2.0.

Hook

Called with

"array"

list

"bool"

bool

"float"

str

"int"

str

"object"

list[tuple[Any, Any]]

"str"

str

Example with numpy:

>>> import jsonyx as json
>>> from functools import partial
>>> import numpy as np
>>> hooks = {
...     "array": partial(np.array, dtype="O"),
...     "bool": np.bool_,
...     "float": np.float64,
...     "int": np.int64
... }
>>> json.loads("[false, 0.0, 0]", hooks=hooks)
array([np.False_, np.float64(0.0), np.int64(0)], dtype=object)

Decoding arbitrary objects

>>> import jsonyx as json
>>> def object_hook(obj):
...     obj = dict(obj)
...     if "__complex__" in obj:
...         return complex(obj["real"], obj["imag"])
...     return obj
...
>>> s = '{"__complex__": true, "real": 1.0, "imag": 2.0}'
>>> json.loads(s, hooks={"object": object_hook})
(1+2j)

Warning

The "object" hook is called with a list of tuples, not a dict.

See also

The pickle and shelve modules which are better suited for this.

Encoding and decoding big integers

>>> import jsonyx as json
>>> from sys import set_int_max_str_digits
>>> set_int_max_str_digits(0)
>>> json.loads("9" * 5_000) == 10 ** 5_000 - 1
True
>>> len(json.dumps(10 ** 5_000))
5002

See Integer string conversion length limitation for more information.