使用python列表理解过滤json响应

n8h*_*rie 2 python json list-comprehension

给定一个带有许多键(属性?)的json对象,如:

[{'name': 'Bob', 'infos': {'spam': 'eggs', 'foo': 'bar'}},
{'name': 'Tom'},
{'name': 'Lisa', 'infos': {'spam': 'qux', 'foo': 'baz'}}
...]
Run Code Online (Sandbox Code Playgroud)

我希望使用列表推导来过滤掉条目 entry['infos']['spam'] == 'eggs'

如果可能的话,我更喜欢列表理解,但到目前为止我唯一的解决方案是使用多个.get()s,最远离树的最远的那个(KeyError通过False在它到达之前做出声明来避免s ).

例如,

# Will obviously fail with KeyError
[each for each in my_json if each['infos']['spam'] == 'eggs']

# Works but requires a separate / additional `.get()`, and only works
# because it is returning False before it evaluates all conditions
[each for each in my_json if each.get('infos') and each.get('infos').get('spam') == 'eggs']

# Fails as all conditions will be evaluated before running
[each for each in my_json if all([each.get('infos'), each.get('infos').get('spam') == 'eggs'])]

# Not a list comprehension, but concise... and also doesn't work
filter(lambda x: x['infos']['spam'] == 'eggs', my_json)
Run Code Online (Sandbox Code Playgroud)

有没有更好的方法来过滤我的json响应?我问的原因是一些API返回带有感兴趣的键的json对象内部深处 ......并且不得不使用类似于each.get('a') and each['a'].get('b') and each['a']['b'].get('c') == 'd'耗尽的东西来验证它each['a']['b']['c'] == 'd'.

我想我总能使用一个try except KeyError.

mylist = []
for each in my_json:
    try:
        if each['infos']['spam'] == 'eggs':
            mylist.append(each)
    except KeyError:
        pass
Run Code Online (Sandbox Code Playgroud)

我是否缺少一个明显的解决方案(最好是在python3标准库中),这将消除所有工作解决方案中的冗余?

Pet*_*son 6

您可以指定默认值get,以防该键不存在任何项目,因此您可以使用

[each for each in my_json if each.get('infos', {}).get('spam') == 'eggs']
Run Code Online (Sandbox Code Playgroud)

第一个get get('infos', {})指定一个空dict作为默认值,这样第二个get就不会失败.

这是一个 filter

>>> filter(lambda x: x.get('infos', {}).get('spam') == 'eggs', my_json)
[{'infos': {'foo': 'bar', 'spam': 'eggs'}, 'name': 'Bob'}]
Run Code Online (Sandbox Code Playgroud)

请注意,如果外部字典中存在"infos",这些仍然会破坏,但本身并不是一个字典.

更健壮的方法是定义过滤函数:

>>> def wonderful_spam(x):
...     try:
...             return x['infos']['spam'] == 'eggs'
...     except (KeyError, TypeError):
...             return False
...
>>> filter(wonderful_spam, my_json)
[{'infos': {'foo': 'bar', 'spam': 'eggs'}, 'name': 'Bob'}]
>>> [x for x in my_json if wonderful_spam(x)]
[{'infos': {'foo': 'bar', 'spam': 'eggs'}, 'name': 'Bob'}]
Run Code Online (Sandbox Code Playgroud)