Skip to content

Instantly share code, notes, and snippets.

@BMDan
Last active December 7, 2022 03:29
Show Gist options
  • Save BMDan/ede923f733dfdf5ed3f6c9634a3e281f to your computer and use it in GitHub Desktop.
Save BMDan/ede923f733dfdf5ed3f6c9634a3e281f to your computer and use it in GitHub Desktop.
from typing import Any, Dict, List, Mapping, Union
# Values for JSON that aren't nested
JSON_v = Union[str, int, float, bool, None]
# If MyPy ever permits recursive definitions, just uncomment this:
# JSON = Union[List['JSON'], Mapping[str, 'JSON'], JSON_v]
# Until then, here's a multi-layer way to represent any (reasonable) JSON we
# might send or receive. It terminates at JSON_4, so the maximum depth of
# the JSON is 5 dicts/lists, like: {'a': {'b': {'c': {'d': {'e': 'f'}}}}}.
JSON_5 = JSON_v
JSON_4 = Union[JSON_v, List[JSON_5], Mapping[str, JSON_5]]
JSON_3 = Union[JSON_v, List[JSON_4], Mapping[str, JSON_4]]
JSON_2 = Union[JSON_v, List[JSON_3], Mapping[str, JSON_3]]
JSON_1 = Union[JSON_v, List[JSON_2], Mapping[str, JSON_2]]
JSON = Union[JSON_v, List[JSON_1], Mapping[str, JSON_1]]
# To allow deeper nesting, you can of course expand the JSON definition above,
# or you can keep typechecking for the first levels but skip typechecking
# at the deepest levels by using UnsafeJSON:
UnsafeJSON_5 = Union[JSON_v, List[Any], Mapping[str, Any]]
UnsafeJSON_4 = Union[JSON_v, List[UnsafeJSON_5], Mapping[str, UnsafeJSON_5]]
UnsafeJSON_3 = Union[JSON_v, List[UnsafeJSON_4], Mapping[str, UnsafeJSON_4]]
UnsafeJSON_2 = Union[JSON_v, List[UnsafeJSON_3], Mapping[str, UnsafeJSON_3]]
UnsafeJSON_1 = Union[JSON_v, List[UnsafeJSON_2], Mapping[str, UnsafeJSON_2]]
UnsafeJSON = Union[JSON_v, List[UnsafeJSON_1], Mapping[str, UnsafeJSON_1]]
@wbolster
Copy link

cool 👍 but shouldn't it be Sequence (abstract type) instead of List (concrete type) for parity with Mapping (abstract type)?

@BMDan
Copy link
Author

BMDan commented Oct 29, 2020

cool 👍 but shouldn't it be Sequence (abstract type) instead of List (concrete type) for parity with Mapping (abstract type)?

It used to be! Problem is, not all Sequences can be serialized into JSON; for example, bytes is an instance of Sequence, but:

> json.dumps({'a': bytes([1,2,3,4])})
<...>
TypeError: Object of type bytes is not JSON serializable

Perhaps some Mappings are also not JSON serializable, but if so I'm not aware of the exceptions. On a practical basis, I used List over Sequence because I didn't want bytes sneaking into things I intended to dump back out as JSON, whereas I went abstract with Mapping because I sometimes had a strong (generally performance-related) preference for an OrderedDict.

If there's a better typing strategy available (JSONSerializable[Sequence] or something?), I'm happy to use it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment