Need to dump a dict of objects. I'd like to sort the keys of the nested objects, while preserving the order of top level keys. The following sorts all keys:
json.dumps(dict_of_objs, sort_keys=True, default=vars)
Thanks
Bad Solution
I could run something like the following but this seems like a lot of unnecessary processing:
for key in dict_of_objs.keys():
sorted_str = json.dumps(dict_of_objs[key], sort_keys=True, default=vars)
dict_of_objs[key] = json.loads(sorted_str)
json.dumps(dict_of_objs)
CodePudding user response:
My solution I think it the same as yours but I think you can just make lt return False.
from json import JSONEncoder
class WrappedDict(dict):
def items(self):
# https://github.com/python/cpython/blob/main/Lib/json/encoder.py#L353
# Return our wrapped items to sorted.
return (WrappedItem(item) for item in super().items())
class WrappedItem(tuple):
def __lt__(self, other):
# https://docs.python.org/3/library/functions.html#sorted
# Fake out sorted by just saying no item, ie. (k, v), is ever less than.
return False
root = {
"b": 2,
"a": 1,
"c": {
"b": 2,
"a": [{
"z": 15,
"a": 1,
}]
}
}
wrapped_root = WrappedDict(root)
print ('root')
print (root)
print ('sort_keys=True with wrapped root')
print (JSONEncoder(sort_keys=True).encode(wrapped_root))
print ('sort_keys=True with root')
print (JSONEncoder(sort_keys=True).encode(root))
print ('sort_keys=False with wrapped_root')
print (JSONEncoder(sort_keys=False).encode(wrapped_root))
print ('sort_keys=False with root')
print (JSONEncoder(sort_keys=False).encode(root))
Output
root
{'b': 2, 'a': 1, 'c': {'b': 2, 'a': [{'z': 15, 'a': 1}]}}
sort_keys=True with wrapped root
{"b": 2, "a": 1, "c": {"a": [{"a": 1, "z": 15}], "b": 2}}
sort_keys=True with root
{"a": 1, "b": 2, "c": {"a": [{"a": 1, "z": 15}], "b": 2}}
sort_keys=False with wrapped_root
{"b": 2, "a": 1, "c": {"b": 2, "a": [{"z": 15, "a": 1}]}}
sort_keys=False with root
{"b": 2, "a": 1, "c": {"b": 2, "a": [{"z": 15, "a": 1}]}}
CodePudding user response:
Edit
Comments pointed out that there's no need to know the values of keys, we can just disable sorting altogether by having lt
always return False
:
#!/usr/bin/env python3
import json
class UnSortableString(str):
def __lt__(self, other):
return False
normal_dict = {"zzz" : [5,4,3], "aaa": [100,3] }
print(json.dumps(normal_dict, sort_keys=True))
mydict = {UnSortableString("zzz") : [5,4,3], UnSortableString("aaa"): [100,3] }
print(json.dumps(mydict, sort_keys=True))
Outputs:
{"aaa": [100, 3], "zzz": [5, 4, 3]}
{"zzz": [5, 4, 3], "aaa": [100, 3]}
Since in my case I know the values of the top level keys, I came up with the following hacky solution:
#!/usr/bin/env python3
import json
class FakeString(str):
order = ["first", "second", "last"]
def __lt__(self, other):
if self in FakeString.order and other in FakeString.order:
return FakeString.order.index(self) < FakeString.order.index(other)
return super.__lt__(self, other)
normal_dict = {"last" : [5,4,3], "second": [100,3] }
print(json.dumps(normal_dict, sort_keys=True))
mydict = {FakeString("last") : [5,4,3], FakeString("second"): [100,3] }
print(json.dumps(mydict, sort_keys=True))
Outputs:
{"last": [5, 4, 3], "second": [100, 3]}
{"second": [100, 3], "last": [5, 4, 3]}