如何使用 json.dumps() 在字典中获取排序列表

can*_*eta 0 python json dictionary list

我有以下问题:有一个像下面这样的python字典:

{"qqq": [{"bbb": "111"}, {"aaa": "333"}], "zzz": {"bbb": [5, 2, 1, 9]}}
Run Code Online (Sandbox Code Playgroud)

我想获得一个有序的 json 对象,例如:

'{"qqq": [{"aaa": "333"}, {"bbb": "111"}], "zzz": {"bbb": [1, 2, 5, 9]}}'
Run Code Online (Sandbox Code Playgroud)

目前我使用以下内容:

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)
Run Code Online (Sandbox Code Playgroud)

但是我对象内的两个列表没有排序,我得到:

'{"qqq": [{"bbb": "111"}, {"aaa": "333"}], "zzz": {"bbb": [5, 2, 1, 9]}}'
Run Code Online (Sandbox Code Playgroud)

可能是因为自定义 JSONEncoder 跳过了一个已经知道如何管理的类型(列表)。

更新

下面的 Martijn 解决方案非常适合上面的示例,但不幸的是,我必须管理更复杂的字典,深度更大:例如以下两个

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']
}
Run Code Online (Sandbox Code Playgroud)

如果对同一列表中的列表进行排序,它们将是相同的。但是它们不是上面的类,我得到了 2 个不同的 json 字符串:

{"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"]}
Run Code Online (Sandbox Code Playgroud)

有什么想法可以解决这个问题吗?

Mar*_*ers 5

default不需要列表;该方法仅适用于编码器不知道如何处理的类型。改写encode方法:

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))
Run Code Online (Sandbox Code Playgroud)

这基本上只是在编码之前对所有列表进行排序(递归);这本可以在将其传递给之前完成,json.dumps()但这样它是编码器责任的一部分,就像对键进行排序一样。

演示:

>>> 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"]}'
Run Code Online (Sandbox Code Playgroud)