如何从嵌套数据结构(例如从解析 JSON)中提取单个值?
- 2025-01-14 08:50:00
- admin 原创
- 104
问题描述:
我编写了一些代码来从 Web API 获取数据。我能够解析来自 API 的 JSON 数据,但得到的结果看起来相当复杂。以下是一个例子:
>>> my_json
{'name': 'ns1:timeSeriesResponseType', 'declaredType': 'org.cuahsi.waterml.TimeSeriesResponseType', 'scope': 'javax.xml.bind.JAXBElement$GlobalScope', 'value': {'queryInfo': {'creationTime': 1349724919000, 'queryURL': 'http://waterservices.usgs.gov/nwis/iv/', 'criteria': {'locationParam': '[ALL:103232434]', 'variableParam': '[00060, 00065]'}, 'note': [{'value': '[ALL:103232434]', 'title': 'filter:sites'}, {'value': '[mode=LATEST, modifiedSince=null]', 'title': 'filter:timeRange'}, {'value': 'sdas01', 'title': 'server'}]}}, 'nil': False, 'globalScope': True, 'typeSubstituted': False}
查看这些数据,我可以看到我想要的具体数据:1349724919000
标记为 的值'creationTime'
。
我怎样才能编写直接获取该值的代码?
我不需要任何搜索逻辑来找到这个值。当我查看响应时,我可以看到我需要什么;我只需要知道如何将其转换为特定代码以硬编码方式提取特定值。我阅读了一些教程,所以我知道我需要使用它[]
来访问嵌套列表和字典的元素;但我无法弄清楚它在复杂情况下的具体工作原理。
更一般地说,我如何才能找出数据的“路径”并为其编写代码?
解决方案 1:
作为参考,让我们看看原始 JSON 的样子,具有漂亮的格式:
>>> print(json.dumps(my_json, indent=4))
{
"name": "ns1:timeSeriesResponseType",
"declaredType": "org.cuahsi.waterml.TimeSeriesResponseType",
"scope": "javax.xml.bind.JAXBElement$GlobalScope",
"value": {
"queryInfo": {
"creationTime": 1349724919000,
"queryURL": "http://waterservices.usgs.gov/nwis/iv/",
"criteria": {
"locationParam": "[ALL:103232434]",
"variableParam": "[00060, 00065]"
},
"note": [
{
"value": "[ALL:103232434]",
"title": "filter:sites"
},
{
"value": "[mode=LATEST, modifiedSince=null]",
"title": "filter:timeRange"
},
{
"value": "sdas01",
"title": "server"
}
]
}
},
"nil": false,
"globalScope": true,
"typeSubstituted": false
}
这让我们更清楚地看到数据的结构。
在具体情况下,我们首先要查看解析数据中键下的相应值。这是另一个字典;我们可以用同样的方式'value'
访问其键的值,然后从那里进行类似操作。'queryInfo'
`'creationTime'`
为了获得所需的值,我们只需将这些访问一个接一个地放置:
my_json['value']['queryInfo']['creationTime'] # 1349724919000
解决方案 2:
我只需要知道如何以硬编码的方式将其转换为特定代码以提取特定值。
如果您再次访问 API,新数据可能不符合代码的预期。您可能会发现添加一些错误处理很有用。例如,用于.get()
访问数据中的字典,而不是索引:
name = my_json.get('name') # will return None if 'name' doesn't exist
另一种方法是明确测试密钥:
if 'name' in resp_dict:
name = resp_dict['name']
else:
pass
但是,如果需要进一步访问,这些方法可能会失败。的占位符结果None
不是字典或列表,因此尝试以这种方式访问它将再次失败(使用TypeError
)。由于“简单胜过复杂”和“请求原谅比请求许可更容易”,因此直接的解决方案是使用异常处理:
try:
creation_time = my_json['value']['queryInfo']['creationTime']
except (TypeError, KeyError):
print("could not read the creation time!")
# or substitute a placeholder, or raise a new exception, etc.
解决方案 3:
以下是从简单 JSON 数据加载单个值并与 JSON 进行相互转换的示例:
import json
# load the data into an element
data={"test1": "1", "test2": "2", "test3": "3"}
# dumps the json object into an element
json_str = json.dumps(data)
# load the json to a string
resp = json.loads(json_str)
# print the resp
print(resp)
# extract an element in the response
print(resp['test1'])
解决方案 4:
尝试一下。
在这里,我仅从COVID API(JSON 数组)中获取状态代码。
import requests
r = requests.get('https://api.covid19india.org/data.json')
x = r.json()['statewise']
for i in x:
print(i['statecode'])
解决方案 5:
尝试一下:
from functools import reduce
import re
def deep_get_imps(data, key: str):
split_keys = re.split("[\\[\\]]", key)
out_data = data
for split_key in split_keys:
if split_key == "":
return out_data
elif isinstance(out_data, dict):
out_data = out_data.get(split_key)
elif isinstance(out_data, list):
try:
sub = int(split_key)
except ValueError:
return None
else:
length = len(out_data)
out_data = out_data[sub] if -length <= sub < length else None
else:
return None
return out_data
def deep_get(dictionary, keys):
return reduce(deep_get_imps, keys.split("."), dictionary)
然后你可以像下面这样使用它:
res = {
"status": 200,
"info": {
"name": "Test",
"date": "2021-06-12"
},
"result": [{
"name": "test1",
"value": 2.5
}, {
"name": "test2",
"value": 1.9
},{
"name": "test1",
"value": 3.1
}]
}
>>> deep_get(res, "info")
{'name': 'Test', 'date': '2021-06-12'}
>>> deep_get(res, "info.date")
'2021-06-12'
>>> deep_get(res, "result")
[{'name': 'test1', 'value': 2.5}, {'name': 'test2', 'value': 1.9}, {'name': 'test1', 'value': 3.1}]
>>> deep_get(res, "result[2]")
{'name': 'test1', 'value': 3.1}
>>> deep_get(res, "result[-1]")
{'name': 'test1', 'value': 3.1}
>>> deep_get(res, "result[2].name")
'test1'