Skip to content

Instantly share code, notes, and snippets.

@dutc
Last active October 10, 2021 12:48
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dutc/b51b093db6ae1b83ea3816eec58f22fb to your computer and use it in GitHub Desktop.
Save dutc/b51b093db6ae1b83ea3816eec58f22fb to your computer and use it in GitHub Desktop.
Extend `json.dumps` to encode namedtuples as maps (not arrays)
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