Last active
October 10, 2021 12:48
-
-
Save dutc/b51b093db6ae1b83ea3816eec58f22fb to your computer and use it in GitHub Desktop.
Extend `json.dumps` to encode namedtuples as maps (not arrays)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from collections import namedtuple | |
from json import dumps | |
from datetime import datetime | |
def fix_default(default=None): | |
class TupleButNotNamedTupleMeta(type): | |
def __instancecheck__(cls, obj): | |
return isinstance(obj, tuple) and not hasattr(obj, '_fields') | |
class TupleButNotNamedTuple(metaclass=TupleButNotNamedTupleMeta): | |
pass | |
def default(obj, default=default): | |
if isinstance(obj, tuple) and not isinstance(obj, TupleButNotNamedTuple): # i.e., it IS a namedtuple... | |
return obj._asdict() | |
if default is not None: | |
return default(obj) | |
raise TypeError('cannot serialize') | |
# patch `json` module | |
from json.encoder import _make_iterencode as mi | |
import json.encoder as enc | |
mi.__defaults__ = (*mi.__defaults__[:8], TupleButNotNamedTuple, *mi.__defaults__[9:]) | |
enc.c_make_encoder = None # disable use of C encoder, which cannot be patched in this way | |
# check | |
try: | |
from collections import namedtuple | |
from json import dumps | |
obj = namedtuple('_', 'x')(0) | |
actual, expected = dumps(obj, default=default), '{"x": 0}' | |
if actual != expected: raise Exception(f'Expected {expected!r}, got {actual!r}') | |
obj = (0, 1, 2) | |
actual, expected = dumps(obj, default=default), '[0, 1, 2]' | |
if actual != expected: raise Exception(f'Expected {expected!r}, got {actual!r}') | |
except Exception as e: | |
raise Exception('JSON encoding hack broke') from e | |
return default | |
if __name__ == '__main__': | |
@fix_default | |
def default(obj): | |
if isinstance(obj, datetime): | |
return obj.isoformat() | |
raise TypeError("cannot serialize") | |
class Foo(namedtuple('Foo', 'a b c, d')): | |
pass | |
class Bar(namedtuple('Bar', 'x y')): | |
pass | |
obj = Foo(Bar(1, 2), (Bar(3, 4), Bar(5, 6)), [Bar(7, 9), Bar(9, 10)], datetime.now()) | |
print(dumps(obj, default=default)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment