2014-10-20 82 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