66 lines
2.3 KiB
Python
66 lines
2.3 KiB
Python
import ctypes
|
|
import json
|
|
import re
|
|
import uuid
|
|
|
|
|
|
class NoIndent(object):
|
|
"""Value wrapper."""
|
|
|
|
def __init__(self, value):
|
|
if not isinstance(value, (list, tuple)):
|
|
raise TypeError("Only lists and tuples can be wrapped")
|
|
self.value = value
|
|
|
|
|
|
class CustomEncoder(json.JSONEncoder):
|
|
FORMAT_SPEC = "@@{}@@"
|
|
regex = re.compile(FORMAT_SPEC.format(r"(\d+)"))
|
|
|
|
def __init__(self, **kwargs):
|
|
# Keyword arguments to ignore when encoding NoIndent wrapped values.
|
|
ignore = {"cls", "indent"}
|
|
|
|
# Save copy of any keyword argument values needed for use here.
|
|
self._kwargs = {k: v for k, v in kwargs.items() if k not in ignore}
|
|
super(CustomEncoder, self).__init__(**kwargs)
|
|
|
|
def default(self, obj):
|
|
if isinstance(obj, NoIndent):
|
|
return self.FORMAT_SPEC.format(id(obj))
|
|
elif isinstance(obj, uuid.UUID):
|
|
return str(obj)
|
|
return super(CustomEncoder, self).default(obj)
|
|
|
|
def iterencode(self, obj, **kwargs):
|
|
format_spec = self.FORMAT_SPEC # Local var to expedite access.
|
|
|
|
# Replace any marked-up NoIndent wrapped values in the JSON repr
|
|
# with the json.dumps() of the corresponding wrapped Python object.
|
|
for encoded in super(CustomEncoder, self).iterencode(obj, **kwargs):
|
|
match = self.regex.search(encoded)
|
|
if match:
|
|
id = int(match.group(1))
|
|
no_indent = ctypes.cast(id, ctypes.py_object).value
|
|
json_repr = json.dumps(no_indent.value, **self._kwargs)
|
|
# Replace the matched id string with json formatted representation
|
|
# of the corresponding Python object.
|
|
encoded = encoded.replace(
|
|
'"{}"'.format(format_spec.format(id)), json_repr
|
|
)
|
|
|
|
yield encoded
|
|
|
|
|
|
class NoIndentByteDecoder(json.JSONDecoder):
|
|
def __init__(self, *args, **kwargs):
|
|
json.JSONDecoder.__init__(self, object_hook=self.object_hook, *args, **kwargs)
|
|
|
|
def object_hook(self, dct):
|
|
if "value" in dct:
|
|
if "values" in dct["value"]:
|
|
if isinstance(dct["value"]["values"], list):
|
|
if isinstance(dct["value"]["values"][0], int):
|
|
dct["value"]["values"] = NoIndent(dct["value"]["values"])
|
|
return dct
|