2014-10-20 79 views
2

过滤JSON响应给出一个JSON对象有许多键,如(属性):与Python列表理解

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

我希望用一个列表理解筛选出的条目,其中entry['infos']['spam'] == 'eggs'

我如果可能的话,我更喜欢列表理解,但到目前为止,我唯一的解决方案是使用多个.get() s,其中最靠右的树最远(以避免KeyError s在声明到达之前通过声明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) 

有没有什么更好的办法来筛选我的JSON响应?我之所以要问的原因是一些API返回的json对象的关键利益下来......不得不使用类似each.get('a') and each['a'].get('b') and each['a']['b'].get('c') == 'd'的东西似乎耗尽只是为了验证each['a']['b']['c'] == 'd'

我想我总是可以使用tryexcept KeyError

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

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

+0

N.B.如果[PEP 463](http://legacy.python.org/dev/peps/pep-0463/)发生,那么您可以在每个['infos'] ['spam']的[my_json =='eggs'除KeyError:False]' – roippi 2014-10-20 00:28:59

回答

4

您可以指定情况下,默认为get项目不存在,该键,因此您可以使用

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

第一个获得get('infos', {})指定了一个空字典为默认设置,以便第二得到的将不是失败。

这是作为一个filter

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

注意这些仍然会将如果“相关信息”外字典存在,但本身不是字典。

更鲁棒的方法是定义一个滤波函数:

>>> 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'}] 
+0

也许'x.get('infos',{})。get('spam',None)'更好?区分一个“垃圾邮件”和一个不含垃圾邮件的字典是很难区分的。但别的很好的答案。 – ssm 2014-10-20 00:54:15

+0

@ssm'None'已经是'get'的默认值,因此您可以省略它;)我将编辑答案 – 2014-10-20 01:00:17

+0

感谢您的快速响应。尽管我使用'.get()'来使用默认值,但我唯一想到的就是'.get('infos',None)'......这没什么区别。给它一个空字典让下一个'.get()'运行是有道理的,谢谢。 – n8henrie 2014-10-21 00:47:23