提问人:ingh.am 提问时间:10/15/2011 最后编辑:Machavityingh.am 更新时间:3/11/2023 访问量:653258
如何在 Python 中解析(读取)和使用 JSON?
How can I parse (read) and use JSON in Python?
问:
我的 Python 程序接收 JSON 数据,我需要从中获取一些信息。如何解析数据并使用结果?我想我需要用于这项任务,但我不明白该怎么做。json.loads
例如,假设我有 .给定这个 JSON 和 的输入,我该如何获取相应的数据?jsonStr = '{"one" : "1", "two" : "2", "three" : "3"}'
"two"
"2"
请注意,.load
用于文件;.loads
用于字符串。另请参阅:从文件中读取 JSON。
有时,JSON 文档旨在表示表格数据。如果您有类似的东西并尝试将其与 Pandas 一起使用,请参阅 Python - 如何将 JSON 文件转换为 Dataframe。
有些数据表面上看起来像 JSON,但不是 JSON。
例如,有时数据来自将 repr
应用于原生 Python 数据结构。结果可能会以不同的方式使用引号,使用标题大小写的 True
和 False
,而不是 JSON 规定的 true
和 false
等。有关此类数据,请参阅将字典的字符串表示形式转换为词典或如何将列表的字符串表示形式转换为列表。
另一种常见的变体格式是在输入的每一行上放置单独的有效 JSON 格式数据。(正确的 JSON 不能逐行解析,因为它使用平衡的括号,可以相隔许多行。这种格式称为 JSONL。请参阅将 JSONL 文件加载为 JSON 对象。
有时,来自 Web 源的 JSON 数据会填充一些额外的文本。在某些情况下,这可以绕过浏览器中的安全限制。这称为 JSONP,在什么是 JSONP,以及为什么创建它?中进行了描述。在其他上下文中,额外的文本会实现一项安全措施,如为什么 Google 会在其 JSON 响应之前附加 while(1) 中所述。无论哪种方式,在 Python 中处理这个问题都很简单:只需识别并删除多余的文本,然后像以前一样继续。
答:
很简单:
import json
data = json.loads('{"one" : "1", "two" : "2", "three" : "3"}')
print(data['two']) # or `print data['two']` in Python 2
评论
import simplejson as json
.load
.loads
有时你的 json 不是一个字符串。例如,如果您从 url 获取 json,如下所示:
j = urllib2.urlopen('http://site.com/data.json')
您将需要使用 json.load,而不是 json.loads:
j_obj = json.load(j)
(很容易忘记:“s”代表“字符串”)
评论
对于 URL 或文件,请使用 .对于包含 .json 内容的字符串,请使用 .json.load()
json.loads()
#! /usr/bin/python
import json
# from pprint import pprint
json_file = 'my_cube.json'
cube = '1'
with open(json_file) as json_data:
data = json.load(json_data)
# pprint(data)
print "Dimension: ", data['cubes'][cube]['dim']
print "Measures: ", data['cubes'][cube]['meas']
以下是可能对您有所帮助的简单示例:
json_string = """
{
"pk": 1,
"fa": "cc.ee",
"fb": {
"fc": "",
"fd_id": "12345"
}
}"""
import json
data = json.loads(json_string)
if data["fa"] == "cc.ee":
data["fb"]["new_key"] = "cc.ee was present!"
print json.dumps(data)
上述代码的输出为:
{"pk": 1, "fb": {"new_key": "cc.ee was present!", "fd_id": "12345",
"fc": ""}, "fa": "cc.ee"}
请注意,您可以设置 dump 的 ident 参数以这样打印它(例如,当使用 print json.dumps(data , indent=4) 时):
{
"pk": 1,
"fb": {
"new_key": "cc.ee was present!",
"fd_id": "12345",
"fc": ""
},
"fa": "cc.ee"
}
解析数据
使用标准库模块json
对于字符串数据,请使用 json.loads
:
import json
text = '{"one" : "1", "two" : "2", "three" : "3"}'
parsed = json.loads(example)
对于来自文件或其他类似文件对象的数据,请使用 json.load
:
import io, json
# create an in-memory file-like object for demonstration purposes.
text = '{"one" : "1", "two" : "2", "three" : "3"}'
stream = io.StringIO(text)
parsed = json.load(stream) # load, not loads
很容易记住区别:代表“字符串”的尾部。(诚然,这可能不符合标准的现代命名实践。s
loads
请注意,不接受文件路径:json.load
>>> json.load('example.txt')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3.8/json/__init__.py", line 293, in load
return loads(fp.read(),
AttributeError: 'str' object has no attribute 'read'
这两个函数都提供了一组相同的附加选项,用于自定义分析过程。从 3.6 开始,选项仅包含关键字。
对于字符串数据,也可以使用库提供的 JSONDecoder
类,如下所示:
import json
text = '{"one" : "1", "two" : "2", "three" : "3"}'
decoder = json.JSONDecoder()
parsed = decoder.decode(text)
可以使用相同的关键字参数,但现在它们被传递给 JSONDecoder
的构造函数,而不是方法。该类的主要优点是它还提供了一个方法,该方法将在 JSON 结束后忽略额外的数据:.decode
.raw_decode
import json
text_with_junk = '{"one" : "1", "two" : "2", "three" : "3"} ignore this'
decoder = json.JSONDecoder()
# `amount` will count how many characters were parsed.
parsed, amount = decoder.raw_decode(text_with_junk)
使用或其他隐式支持requests
当使用流行的第三方库从 Internet 检索数据时,无需从对象中提取(或创建任何类型的类似文件的对象)并单独解析它。相反,该对象直接提供了一个方法,该方法将执行以下解析:requests
.text
Response
Response
.json
import requests
response = requests.get('https://www.example.com')
parsed = response.json()
此方法接受与标准库功能相同的关键字参数。json
使用结果
默认情况下,使用上述任何一种方法进行解析都会产生一个完全普通的 Python 数据结构,该结构由完全普通的内置类型 、 、 、 、 (JSON 并成为 Python 常量和 ) 和 (JSON 成为 Python 常量 ) 组成。dict
list
str
int
float
bool
true
false
True
False
NoneType
null
None
因此,使用此结果的工作方式与使用任何其他技术获得相同数据的方式相同。
因此,继续问题中的例子:
>>> parsed
{'one': '1', 'two': '2', 'three': '3'}
>>> parsed['two']
'2'
我之所以强调这一点,是因为许多人似乎认为结果有一些特别之处;没有。它只是一个嵌套的数据结构,尽管处理嵌套有时很难理解。
例如,考虑一个解析的结果,如 。要获得,需要一次一个地遵循适当的步骤:在字典中查找密钥会给出一个列表;该列表的第二个元素(索引)是 ;在那里查找密钥会给出价值。因此,相应的代码是:每个索引步骤都按顺序应用。result = {'a': [{'b': 'c'}, {'d': 'e'}]}
'e'
a
[{'b': 'c'}, {'d': 'e'}]
1
{'d': 'e'}
'd'
'e'
result['a'][1]['d']
另请参阅如何从嵌套数据结构中提取单个值(例如从解析 JSON 中提取)?。
有时人们希望应用更复杂的选择标准、遍历嵌套列表、过滤或转换数据等。这些是更复杂的主题,将在其他地方处理。
混淆的常见来源
JSON 类似对象
在尝试解析 JSON 数据之前,请务必确保数据实际上是 JSON。检查 JSON 格式规范以验证预期内容。要点:
文档表示一个值(通常是一个 JSON“对象”,对应于 Python ,但 JSON 表示的所有其他类型都是允许的)。特别是,它没有在每一行上有一个单独的条目 - 即 JSONL。
dict
使用标准文本编码(通常为 UTF-8)后,数据是人类可读的。几乎所有文本都包含在双引号内,并在适当的情况下使用转义序列。
处理嵌入数据
请考虑一个包含以下内容的示例文件:
{"one": "{\"two\": \"three\", \"backslash\": \"\\\\\"}"}
此处的反斜杠用于 JSON 的转义机制。 当使用上述方法之一进行解析时,我们得到的结果如下:
>>> example = input()
{"one": "{\"two\": \"three\", \"backslash\": \"\\\\\"}"}
>>> parsed = json.loads(example)
>>> parsed
{'one': '{"two": "three", "backslash": "\\\\"}'}
请注意,这是一个 str
,而不是 .但是,碰巧的是,该字符串本身表示“嵌入式”JSON 数据。parsed['one']
dict
要用解析结果替换嵌入数据,只需访问数据,使用相同的解析技术,然后从那里继续(例如,通过更新原始结果):
>>> parsed['one'] = json.loads(parsed['one'])
>>> parsed
{'one': {'two': 'three', 'backslash': '\\'}}
请注意,此处的部分是包含一个实际反斜杠的字符串的表示形式,而不是两个。这遵循了字符串转义的常用 Python 规则,这使我们...'\\'
JSON 转义与 Python 字符串文字转义
有时,人们在尝试测试涉及解析 JSON 的代码时会感到困惑,并在 Python 源代码中将输入作为不正确的字符串文字提供。在尝试测试需要使用嵌入式 JSON 的代码时,尤其会发生这种情况。
问题在于 JSON 格式和字符串文本格式都有单独的数据转义策略。Python 将处理字符串文字中的转义以创建字符串,然后该字符串仍需要包含 JSON 格式使用的转义序列。
在上面的示例中,我使用了 at 解释器提示符来显示示例数据,以避免与转义混淆。下面是一个在源中使用字符串文字的类似示例:input
>>> json.loads('{"one": "{\\"two\\": \\"three\\", \\"backslash\\": \\"\\\\\\\\\\"}"}')
{'one': '{"two": "three", "backslash": "\\\\"}'}
若要改用双引号字符串文本,还需要对字符串文本中的双引号进行转义。因此:
>>> json.loads('{\"one\": \"{\\\"two\\\": \\\"three\\\", \\\"backslash\\\": \\\"\\\\\\\\\\\"}\"}')
{'one': '{"two": "three", "backslash": "\\\\"}'}
输入中的每个序列都成为实际的 JSON 数据,当 JSON 解析器解析时,该数据将(嵌入在字符串中)。类似地,在实际的 JSON 数据中,(五对反斜杠,然后是一个转义引号)变成(五个反斜杠和一个引号;相当于两对反斜杠,然后是一个转义引号),当 JSON 解析器解析时,它变成(两个反斜杠和一个引号),在解析结果的字符串表示中变成(两个转义反斜杠和一个引号)(从现在开始, 引号不需要转义,因为 Python 可以对字符串使用单引号;但反斜杠仍然如此)。\\\"
\"
"
\\\\\\\\\\\"
\\\\\"
\\"
\\\\"
简单定制
除了选项之外,可用于 AND 的关键字选项应该是回调。解析器将调用它们,传入部分数据,并使用返回的任何内容来创建整体结果。strict
json.load
json.loads
“解析”钩子是相当不言自明的。例如,我们可以指定将浮点值转换为实例,而不是使用原生 Python :decimal.Decimal
float
>>> import decimal
>>> json.loads('123.4', parse_float=decimal.Decimal)
Decimal('123.4')
或者对每个值使用浮点数,即使它们可以转换为整数:
>>> json.loads('123', parse_int=float)
123.0
或拒绝转换特殊浮点值的 JSON 表示形式:
>>> def reject_special_floats(value):
... raise ValueError
...
>>> json.loads('Infinity')
inf
>>> json.loads('Infinity', parse_constant=reject_special_floats)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3.8/json/__init__.py", line 370, in loads
return cls(**kw).decode(s)
File "/usr/lib/python3.8/json/decoder.py", line 337, in decode
obj, end = self.raw_decode(s, idx=_w(s, 0).end())
File "/usr/lib/python3.8/json/decoder.py", line 353, in raw_decode
obj, end = self.scan_once(s, idx)
File "<stdin>", line 2, in reject_special_floats
ValueError
使用 和 的自定义示例object_hook
object_pairs_hook
object_hook
并且可用于控制解析器在给定 JSON 对象时执行的操作,而不是创建 Python 。
提供的将使用一个参数进行调用,该参数是键值对的列表,否则将用于 .它应该返回所需的结果或其他结果:object_pairs_hook
dict
object_pairs_hook
dict
dict
>>> def process_object_pairs(items):
... return {k: f'processed {v}' for k, v in items}
...
>>> json.loads('{"one": 1, "two": 2}', object_pairs_hook=process_object_pairs)
{'one': 'processed 1', 'two': 'processed 2'}
相反,将调用一个 否则将创建的,结果将替换:object_hook
dict
>>> def make_items_list(obj):
... return list(obj.items())
...
>>> json.loads('{"one": 1, "two": 2}', object_hook=make_items_list)
[('one', 1), ('two', 2)]
如果两者都提供,则将忽略 ,仅使用 。object_hook
object_items_hook
文本编码问题和/或混淆bytes
unicode
从根本上说,JSON是一种文本格式。在分析文件之前,应首先使用适当的编码将输入数据从原始字节转换为文本。
在 3.x 中,支持从对象加载,并将隐式使用 UTF-8 编码:bytes
>>> json.loads('"text"')
'text'
>>> json.loads(b'"text"')
'text'
>>> json.loads('"\xff"') # Unicode code point 255
'ÿ'
>>> json.loads(b'"\xff"') # Not valid UTF-8 encoded data!
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3.8/json/__init__.py", line 343, in loads
s = s.decode(detect_encoding(s), 'surrogatepass')
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 1: invalid start byte
UTF-8 通常被认为是 JSON 的默认值。虽然原始规范 ECMA-404 没有强制要求编码(它只描述“JSON 文本”,而不是 JSON 文件或文档),但 RFC 8259 要求:
在不属于封闭生态系统的系统之间交换的 JSON 文本必须使用 UTF-8 [RFC3629] 进行编码。
在这样的“封闭生态系统”中(即对于编码不同且不会公开共享的本地文档),首先显式应用适当的编码:
>>> json.loads(b'"\xff"'.decode('iso-8859-1'))
'ÿ'
同样,JSON 文件应以文本模式打开,而不是以二进制模式打开。如果文件使用不同的编码,只需在打开文件时指定:
with open('example.json', encoding='iso-8859-1') as f:
print(json.load(f))
在 2.x 中,字符串和字节序列没有正确区分,这导致了很多问题和混淆,尤其是在使用 JSON 时。
积极维护的 2.x 代码库(请注意,2.x 本身自 2020 年 1 月 1 日以来一直没有维护)应始终如一地使用值来表示文本和值来表示原始数据(在 2.x 中是别名),并接受 of 值将具有前缀(毕竟,代码应该关注值的实际是什么, 不是在 REPL 上的样子)。unicode
str
str
bytes
repr
unicode
u
历史说明:simplejson
simplejson
只是标准库的json
模块,但是在外部维护和开发。它最初是在将 JSON 支持添加到 Python 标准库之前创建的。在 2.6 中,该项目作为 .当前的开发将兼容性保持在 2.5 的水平,尽管还有一个未维护的遗留分支应该支持最早的 2.2。simplejson
json
标准库通常使用相当旧版本的包;例如,我的 3.8.10 安装报告
>>> json.__version__
'2.0.9'
而最新版本(截至撰写本文时)是 3.18.1。(Github 存储库中标记的版本可以追溯到 3.8.2;2.0.9 版本可以追溯到 2009 年。
到目前为止,我还无法找到哪些版本对应于哪些 Python 版本的完整文档。simplejson
评论
“
分隔符。如果是这样,请修复生成该输出的代码以代替 or ,然后转到将字典的字符串表示转换为字典?,以了解如何恢复 Python 数据。你有 Python 文字的其他线索吗?查找 ,或 ,JSON 将使用 , & 。'
json.dumps()
str()
repr()
None
True
False
null
true
false
'