I have the following problem: having a python dictionary like the following:
{"qqq": [{"bbb": "111"}, {"aaa": "333"}], "zzz": {"bbb": [5, 2, 1, 9]}}
I would like to obtain an ordered json object such as:
'{"qqq": [{"aaa": "333"}, {"bbb": "111"}], "zzz": {"bbb": [1, 2, 5, 9]}}'
At the moment I use the following:
class ListEncoder(json.JSONEncoder): def default(self, o): if isinstance(o, list): return sorted(o) return json.JSONEncoder.default(self, o) print json.dumps(c, sort_keys=True, cls=ListEncoder)
But the two list inside my object are not sorted, and I get:
'{"qqq": [{"bbb": "111"}, {"aaa": "333"}], "zzz": {"bbb": [5, 2, 1, 9]}}'
probably because the custom JSONEncoder skips a type that already knows how to manage (list).
UPDATE
Martijn solution below works perfectly for the example above, but unfortunately I have to manage more complicated dictionaries, with a bigger depth: for example the following two
a = { 'aaa': 'aaa', 'op': 'ccc', 'oppa': { 'ggg': [{'fff': 'ev'}], 'flt': { 'nnn': [ { 'mmm': [{'a_b_d': [6]},{'a_b_c': [6,7]}] }, { 'iii': [3, 2, 4, 5] } ] } }, 'rrr': {}, 'ttt': ['aaa-bbb-ccc'] } b = { 'aaa': 'aaa', 'op': 'ccc', 'oppa': { 'ggg': [{'fff': 'ev'}], 'flt': { 'nnn': [ { 'iii': [2, 3, 4, 5] }, { 'mmm': [{'a_b_c': [6,7]},{'a_b_d': [6]}] } ] } }, 'rrr': {}, 'ttt': ['aaa-bbb-ccc'] }
They would be the same if the lists inside the same would be sorted. But they aren’t with the class above, and I get 2 different json strings:
{"aaa": "aaa", "op": "ccc", "oppa": {"flt": {"nnn": [{"iii": [3, 2, 4, 1]}, {"mmm": [{"a_b_d": [6]}, {"a_b_c": [6, 7]}]}]}, "ggg": [{"fff": "ev"}]}, "rrr": {}, "ttt": ["aaa-bbb-ccc"]} {"aaa": "aaa", "op": "ccc", "oppa": {"flt": {"nnn": [{"iii": [2, 3, 4, 5]}, {"mmm": [{"a_b_c": [6, 7]}, {"a_b_d": [6]}]}]}, "ggg": [{"fff": "ev"}]}, "rrr": {}, "ttt": ["aaa-bbb-ccc"]}
Any idea to fix this?
Advertisement
Answer
default
isn’t called for lists; that method is only for types the encoder doesn’t know how to handle. Override the encode
method instead:
class SortedListEncoder(json.JSONEncoder): def encode(self, obj): def sort_lists(item): if isinstance(item, list): return sorted(sort_lists(i) for i in item) elif isinstance(item, dict): return {k: sort_lists(v) for k, v in item.items()} else: return item return super(SortedListEncoder, self).encode(sort_lists(obj))
This essentially just sorts all lists (recursively) before encoding; this could have been done before passing it to json.dumps()
but this way it is part of the responsibility of the encoder, just like sorting the keys is.
Demo:
>>> json.dumps(c, sort_keys=True, cls=SortedListEncoder) '{"qqq": [{"aaa": "333"}, {"bbb": "111"}], "zzz": {"bbb": [1, 2, 5, 9]}}' >>> json.dumps(a, sort_keys=True, cls=SortedListEncoder) '{"aaa": "aaa", "op": "ccc", "oppa": {"flt": {"nnn": [{"iii": [2, 3, 4, 5]}, {"mmm": [{"a_b_c": [6, 7]}, {"a_b_d": [6]}]}]}, "ggg": [{"fff": "ev"}]}, "rrr": {}, "ttt": ["aaa-bbb-ccc"]}' >>> json.dumps(b, sort_keys=True, cls=SortedListEncoder) '{"aaa": "aaa", "op": "ccc", "oppa": {"flt": {"nnn": [{"iii": [2, 3, 4, 5]}, {"mmm": [{"a_b_c": [6, 7]}, {"a_b_d": [6]}]}]}, "ggg": [{"fff": "ev"}]}, "rrr": {}, "ttt": ["aaa-bbb-ccc"]}'