import json
[docs]class CompactEncoder(json.JSONEncoder):
'''
JSONEncoder can be used as `cls` argument to `json.dump` and `json.dumps`.
It creates formatted JSON strings, with indentation that are more compact
than the dafault formatting from `json` module. The main difference is that
the lists of primitives are not split into multiple lines.
'''
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.indent = -1
self.respect_indent = True
def _is_primitive(self, obj):
return isinstance(obj, (int, bool, str, float))
[docs] def encode(self, obj):
'''
Return a JSON string representation of a Python data structure.
Example:
>>> CompactEncoder().encode({"foo": ["bar", "baz"]})
'{\\n\\t"foo": ["bar", "baz"]\\n}'
'''
return ''.join([i for i in self.iterencode(obj)])
[docs] def iterencode(self, obj):
'''
Encode the given object and yield each string representation line by
line.
Example:
>>> item = {"foo": ["bar", "baz"]}
>>> ''.join(list(CompactEncoder().iterencode(item))) == \\
... CompactEncoder().encode(item)
True
'''
self.indent += 1
if self.respect_indent:
ind = self.indent*'\t'
else:
ind = ''
if isinstance(obj, dict):
if len(obj) == 0:
yield f"{ind}{{}}"
else:
body = []
for k, v in obj.items():
body.extend([
f'{j[:self.indent]}{json.dumps(k)}: {j[self.indent:]}'
for j in self.iterencode(v)
])
body_str = ",\n".join(body)
yield (
f'{ind}{{\n'
f'{body_str}\n'
f'{ind}}}'
)
elif isinstance(obj, (list, tuple)):
primitive_list = True
for i in obj:
if not self._is_primitive(i):
primitive_list = False
break
if primitive_list:
body = []
self.respect_indent = False
for i in obj:
body.extend([j for j in self.iterencode(i)])
self.respect_indent = True
yield f'{ind}[{", ".join(body)}]'
else:
body = []
for i in obj:
body.extend([j for j in self.iterencode(i)])
body_str = ",\n".join(body)
yield (
f'{ind}[\n'
f'{body_str}\n'
f'{ind}]'
)
elif self._is_primitive(obj):
if isinstance(obj, str):
yield f'{ind}{json.dumps(obj)}'
else:
yield f'{ind}{str(obj).lower()}'
elif obj is None:
yield f'{ind}null'
else:
raise TypeError('Object of type set is not JSON serializable')
self.indent -= 1