![](/img/trans.png)
[英]How to return specific key value pairs from this Json readable extract in Python when key value pair is deeply nested?
[英]How to return specific key value pair from nested json without knowing location?
我想在不知道確切位置的情況下獲取嵌套 json 文件中特定鍵的值。 所以基本上查看所有鍵(和嵌套鍵)直到找到匹配項,並返回字典 {match: "value"} 嵌套json_data
:
{
"$id": "1",
"DataChangedEntry": {
"$id": "2",
"PathProperty": "/",
"Metadata": null,
"PreviousValue": null,
"CurrentValue": {
"CosewicWsRefId": {
"Value": "QkNlrjq2HL9bhTQqU8-qH"
},
"Date": {
"Value": "2022-05-20T00:00:00Z"
},
"YearSentToMinister": {
"Value": "0001-01-01T00:00:00"
},
"DateSentToMinister": {
"Value": "0001-01-01T00:00:00"
},
"Order": null,
"Type": {
"Value": "REGULAR"
},
"ReportType": {
"Value": "NEW"
},
"Stage": {
"Value": "ASSESSED"
},
"State": {
"Value": "PUBLISHED"
},
"StatusAndCriteria": {
"Status": {
"Value": "EXTINCT"
},
"StatusComment": {
"EnglishText": null,
"FrenchText": null
},
"StatusChange": {
"Value": "NOT_INITIALIZED"
},
"StatusCriteria": {
"EnglishText": null,
"FrenchText": null
},
"ApplicabilityOfCriteria": {
"ApplicabilityCriteriaList": []
}
},
"Designation": null,
"Note": null,
"DomainEvents": [],
"Version": {
"Value": 1651756761385.1248
},
"Id": {
"Value": "3z3XlCkaXY9xinAbK5PrU"
},
"CreatedAt": {
"Value": 1651756761384
},
"ModifiedAt": {
"Value": 1651756785274
},
"CreatedBy": {
"Value": "G@a"
},
"ModifiedBy": {
"Value": "G@a"
}
}
},
"EventAction": "Create",
"EventDataChange": {
"$ref": "2"
},
"CorrelationId": "3z3XlCkaXY9xinAbK5PrU",
"EventId": "WGxlewsUAHayLHZ2LHvFk",
"EventTimeUtc": "2022-05-06T13:15:31.7463355Z",
"EventDataVersion": "1.0.0",
"EventType": "AssessmentCreatedInfrastructure"
}
期望的返回值是來自 json_data["DataChangedEntry"]["CurrentValue"]["Date"]["Value"] 的值:
"2022-05-20T00:00:00Z"
到目前為止,我已經嘗試了一個遞歸函數,但它一直返回None
:
match_dict = {}
def recursive_json(data,attr,m_dict):
for k,v in data.items():
if k == attr:
for k2,v2 in v.items():
m_dict = {attr, v2}
print('IF: ',m_dict)
return m_dict
elif isinstance(v,dict):
return recursive_json(v,attr,m_dict)
print('RETURN: ',recursive_json(json_data, "Date", match_dict))
輸出:
RETURN: None
我嘗試刪除第二個return
語句,它現在在函數中打印我想要的值,但仍然返回None
:
match_dict = {}
def recursive_json(data,attr,m_dict):
for k,v in data.items():
if k == attr:
for k2,v2 in v.items():
m_dict = {attr, v2}
print('IF: ',m_dict)
return m_dict
elif isinstance(v,dict):
recursive_json(v,attr,m_dict)
print('RETURN: ',recursive_json(json_data, "Date", match_dict))
輸出:
IF: {'Date', '2022-05-20T00:00:00Z'}
RETURN: None
我不明白為什么它一直返回None
。 有沒有更好的方法來返回我想要的值?
潛在的問題是:我們如何在一個循環中進行多次遞歸調用,如果其中任何一個返回有用的東西,則return
遞歸結果,否則失敗?
如果我們盲目地在循環內return
,那么只能進行一次遞歸調用。 無論它返回什么,都會在這個級別返回。 如果它沒有找到有用的結果,我們就得不到有用的結果。
如果我們一味地不在循環內返回,那么返回的值就無關緊要了。 當前調用中沒有任何東西使用它們,所以我們將完成循環,進行所有遞歸調用,到達函數的末尾......因此隱式返回None
。
當然,解決這個問題的方法是檢查遞歸調用是否返回了有用的東西。 如果是這樣,我們可以返回它; 否則,我們繼續前進。 如果我們到達終點,那么我們會發出信號,表明我們找不到任何有用的東西——這樣,如果我們被遞歸調用,調用者可以做正確的事情。
假設None
不能是“有用”的值,我們自然可以將其用作信號。 我們甚至不必在最后顯式地返回它。
在修正了一些其他的錯別字之后(我們不應該覆蓋全局內置的dict
名稱,而且無論如何我們不需要命名我們在開始時傳入的dict,並且參數應該是m_dict
以便在我們正確定義時進行遞歸調用),我們得到:
def recursive_json(data, attr, m_dict):
for k,v in data.items():
if k == attr:
for k2,v2 in v.items():
m_dict = {attr, v2}
print('IF: ', m_dict)
return m_dict
elif isinstance(v,dict):
result = recursive_json(v, attr, m_dict)
if result:
return result
# call it:
recursive_json(json_data, "Date", {})
我們可以看到打印了調試跟蹤,並且也返回了值。
讓我們稍微改進一下:
首先for k2,v2 in v.items():
沒有任何意義。 同樣,我們每次調用只能return
一次,因此這將跳過第一個之后的字典中的任何值。 直接返回v
會更好。 此外, m_dict
參數實際上並不能幫助實現邏輯; 我們不會在調用之間修改它。 使用set
作為我們的返回值是沒有意義的,因為它基本上是無序的; 我們關心這里的順序。 最后,我們不再需要調試跟蹤。 這給了我們:
def recursive_json(data, attr):
for k, v in data.items():
if k == attr:
return attr, v
elif isinstance(v,dict):
result = recursive_json(v, attr)
if result:
return result
為了更漂亮,我們可以將基本案例與遞歸案例分開,並為每個案例使用更優雅的工具。 要檢查是否有任何鍵匹配,我們可以簡單地使用in
運算符進行檢查。 要遞歸並返回第一個富有成效的結果,內置的next
很有用。 我們得到:
def recursive_json(data, attr):
if not isinstance(data, dict):
# reached a leaf, can't search in here.
return None
if attr in data:
return k, data[k]
candidates = (recursive_json(v, attr) for v in data.values())
try:
# the first non-None candidate, if any.
return next(c for c in candidates if c is not None)
except StopIteration:
return None # all candidates were None.
看起來你正在嘗試寫這樣的東西:
from json import loads
from typing import Any
test_json = """
{
"a": {
"b": {
"value": 1
}
},
"b": {
"value": 2
},
"c": {
"b": {
"value": 3
},
"c": {
"value": 4
}
},
"d": {}
}
"""
json_data = loads(test_json)
def find_value(data: dict, attr: str, depth_first: bool=True) -> (bool, Any):
# assumes data is a dict, with 'value' attributes for the attr to be found
# returns [whether value was found]: bool, [actual value]: Any
for k, v in data.items():
if k == attr and 'value' in v:
return True, v['value']
elif depth_first and isinstance(v, dict):
if (t := find_value(v, attr, depth_first))[0]:
return t
if not depth_first:
for _, v in data.items():
if isinstance(v, dict) and (t := find_value(v, attr, depth_first))[0]:
return t
return False, None
# returns True, 1 - first 'b' with a 'value', depth-first
print(find_value(json_data, 'b'))
# returns True, 2 - first 'b' with a 'value', breadth-first
print(find_value(json_data, 'b', False))
# returns True, 4 - first 'c' with a 'value' - the 'c' at the root level has no 'value'
print(find_value(json_data, 'c'))
# returns False, None - no 'd' with a value
print(find_value(json_data, 'd'))
# returns False, None - no 'e' in data
print(find_value(json_data, 'e'))
您自己的函數可以返回None
因為您實際上並沒有返回遞歸調用將返回的值。 函數的默認返回值為None
。
但是,您的代碼也沒有考慮到找不到任何東西的情況。
(注意:此解決方案僅適用於 Python 3.8 或更高版本,因為它使用了海象運算符:=
- 當然,如果沒有它,編寫它並不難,但這留給讀者作為練習
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.