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

时间:2014-10-20 00:18:51

标签: python json list-comprehension

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

[{'name': 'Bob', 'infos': {'spam': 'eggs', 'foo': 'bar'}},
{'name': 'Tom'},
{'name': 'Lisa', 'infos': {'spam': 'qux', 'foo': 'baz'}}
...]

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

的条目

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

例如,

# 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)

有没有更好的方法来过滤我的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

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

1 个答案:

答案 0 :(得分:5)

如果该密钥不存在任何项目,您可以指定get的默认值,因此您可以使用

[each for each in my_json if each.get('infos', {}).get('spam') == 'eggs']

第一个获取get('infos', {})指定一个空的dict作为默认值,以便第二个获胜失败。

此处为filter

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

请注意,如果" 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'}]