I have a Python object tree I want to serialize. Its structure is something like this:
{
"regular_object": {
...
},
"regular_field": ...
"special_object": {
"f1": "v1",
"f2": v2,
...
"fn": "vn"
}
}
I wanna serialize such object tree so that it's formatted like this in a JSON representation:
{
"regular_object": {
...
},
"regular_field": ...
"special_object": { "f1": "v1", "f2": v2, ..., "fn": "vn" }
}
In other words, I want mixed formatting based on the contents of the object being formatted .
I tried playing around with json.JSONEncoder
by overriding its default
method and using a custom class object for storing the special_object
's contents so that the custom default
is called, something like this:
class SpecialObject:
def __init__(self, attrs: dict):
self.attrs = attrs
class SpecialEncoder(json.JSONEncoder):
def default(self, o):
JE = json.JSONEncoder
if isinstance(o, SpecialObject):
return f"{{ ', '.join([f'{JE.encode(self, el[0])}: {JE.encode(el[1])}' for el in o.attrs.items()]) }}"
else:
return super().default(o)
But it looks like JSONEncoder
encodes the default
's return value again , so the string I generate for my custom type gets serialized as a string, with the output being something like:
{
...
"special_object": "{ \"f1\": \"v1\", ..., \"fn\": \"vn\" }"
}
which is clearly not what I had in mind. I also tried overriding the JSONEncoder.encode
method like some answers here suggest, but that method never gets invoked from within JSONEncoder
itself.
I did find several approaches to solving seemingly similar problems, but the solutions presented there are way too monstrously looking and overly complex for a task like mine. Is there a way to achieve what I need in just one-two dozen lines of code without going too deep into JSONEncoder
's intestines? It does not need to be a generic solution, just something I could fix quickly and which may work for a single case only.
Actually, you can do something like this, but it's ... you see how it looks.
def custom_json_dumps(obj, *args, keys_to_skip_indent=(), **kwargs):
if isinstance(obj, dict):
indent = kwargs.pop("indent", 0)
separators = kwargs.get("separators", (", ", ": "))
res = "{" + "\n" * int(indent != 0)
for k, v in obj.items():
if k in keys_to_skip_indent or indent == 0:
encoded = json.dumps(v, *args, **kwargs)
else:
encoded = json.dumps(v, *args, indent=indent, **kwargs)
res += "\"{}\"".format(k) + separators[1] + encoded + separators[0] + "\n" * int(indent != 0)
return res[:res.rindex(separators[0])].replace("\n", "\n" + " " * indent) + "\n" * int(indent != 0) + "}"
else:
return json.dumps(obj, *args, **kwargs)
Test:
o = {
"regular_object": {
"a": "b"
},
"regular_field": 100000,
"float_test": 1.0000001,
"bool_test": True,
"list_test": ["1", 0, 1.32, {"a": "b"}],
"special_object": {
"f1": "v1",
"f2": "v2",
"fn": "vn"
}
}
print(custom_json_dumps(o, keys_to_skip_indent={"special_object", "list_test"}, indent=4))
Output:
{
"regular_object": {
"a": "b"
},
"regular_field": 100000,
"float_test": 1.0000001,
"bool_test": true,
"list_test": ["1", 0, 1.32, {"a": "b"}],
"special_object": {"f1": "v1", "f2": "v2", "fn": "vn"}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.