如何在 Python 中解析(读取)和使用 JSON?

How can I parse (read) and use JSON in Python?

提问人:ingh.am 提问时间:10/15/2011 最后编辑:Machavityingh.am 更新时间:3/11/2023 访问量:652937

问:

我的 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 数据结构。结果可能会以不同的方式使用引号,使用标题大小写的 TrueFalse,而不是 JSON 规定的 truefalse,等等。有关此类数据,请参阅将字典的字符串表示形式转换为字典或如何将列表的字符串表示形式转换为列表

另一种常见的变体格式在输入的每一上放置单独的有效 JSON 格式数据。(正确的 JSON 不能逐行解析,因为它使用可以相隔许多行的平衡括号。这种格式称为 JSONL。请参阅将 JSONL 文件加载为 JSON 对象

有时,来自 Web 源的 JSON 数据会填充一些额外的文本。在某些情况下,这可以绕过浏览器中的安全限制。这称为 JSONP,在什么是 JSONP,以及为什么创建它?中进行了描述。在其他上下文中,额外的文本会实现安全措施,如为什么 Google 会将 while(1); 添加到其 JSON 响应中所述。无论哪种方式,在 Python 中处理这个问题都很简单:只需识别并删除多余的文本,然后像以前一样继续。

Python JSON 解析

评论

15赞 Martijn Pieters 11/22/2018
注意:对于那些使用单引号字符串分隔符的数据,您可能不小心为 Python 字典创建了字符串表示形式。JSON 将始终使用 分隔符。如果是这样,请修复生成该输出的代码以代替 or ,然后转到将字典的字符串表示转换为字典?,以了解如何恢复 Python 数据。你有 Python 文字的其他线索吗?查找 ,或 ,JSON 将使用 , & 。'json.dumps()str()repr()NoneTrueFalsenulltruefalse
0赞 questionto42 5/28/2020
那些没有 jsonStr 但有字典列表(可能带有单引号字符串分隔符)的人也可以在这里看看:stackoverflow.com/questions/41168558/...'

答:

634赞 John Giotta 10/15/2011 #1

很简单:

import json
data = json.loads('{"one" : "1", "two" : "2", "three" : "3"}')
print(data['two'])  # or `print data['two']` in Python 2

评论

2赞 unode 10/15/2011
另外,如果您需要更好的性能,请查看 simplejson。较新的版本提供了优化,大大提高了读取和写入能力。
4赞 ingh.am 10/16/2011
我实际上已经在使用simplejson了:.忘了提,但谢谢:)import simplejson as json
0赞 Sunil Kumar 9/21/2018
明白了。使用方法而不是.load.loads
99赞 jisaacstone 10/15/2011 #2

有时你的 json 不是一个字符串。例如,如果您从 url 获取 json,如下所示:

j = urllib2.urlopen('http://site.com/data.json')

您将需要使用 json.load,而不是 json.loads:

j_obj = json.load(j)

(很容易忘记:“s”代表“字符串”)

评论

1赞 rkachach 1/15/2016
只是为了补充一点,您可以通过调用 j.read() 然后使用 loads 方法来获取字符串内容。无论如何,在这种情况下,load() 方法都会负责调用 .read()
79赞 Mohammad Shahid Siddiqui 7/6/2013 #3

对于 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']
31赞 Venkat 9/30/2015 #4

以下是可能对您有所帮助的简单示例:

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"
}
6赞 Karl Knechtel 1/19/2023 #5

解析数据

使用标准库模块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

很容易记住区别:代表“字符串”的尾部。(诚然,这可能不符合标准的现代命名实践。sloads

请注意,接受文件路径: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.textResponseResponse.json

import requests
response = requests.get('https://www.example.com')
parsed = response.json()

此方法接受与标准库功能相同的关键字参数。json

使用结果

默认情况下,使用上述任何一种方法进行解析都会产生一个完全普通的 Python 数据结构,该结构由完全普通的内置类型 、 、 、 、 (JSON 并成为 Python 常量和 ) 和 (JSON 成为 Python 常量 ) 组成。dictliststrintfloatbooltruefalseTrueFalseNoneTypenullNone

因此,使用此结果的工作方式与使用任何其他技术获得相同数据的方式相同

因此,继续问题中的例子:

>>> 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 的关键字选项应该是回调。解析器将调用它们,传入部分数据,并使用返回的任何内容来创建整体结果。strictjson.loadjson.loads

“解析”钩子是相当不言自明的。例如,我们可以指定将浮点值转换为实例,而不是使用原生 Python :decimal.Decimalfloat

>>> 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_hookobject_pairs_hook

object_hook并且可用于控制解析器在给定 JSON 对象时执行的操作,而不是创建 Python 。 提供的将使用一个参数进行调用,该参数是键值对的列表,否则将用于 .它应该返回所需的结果或其他结果:object_pairs_hookdictobject_pairs_hookdictdict

>>> 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_hookdict

>>> 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_hookobject_items_hook

文本编码问题和/或混淆bytesunicode

从根本上说,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 上的样子)。unicodestrstrbytesreprunicodeu

历史说明:simplejson

simplejson 只是标准库的json模块,但是在外部维护和开发。它最初是在将 JSON 支持添加到 Python 标准库之前创建的。在 2.6 中,该项目作为 .当前的开发将兼容性保持在 2.5 的水平,尽管还有一个未维护的遗留分支应该支持最早的 2.2。simplejsonjson

标准库通常使用相当旧版本的包;例如,我的 3.8.10 安装报告

>>> json.__version__
'2.0.9'

而最新版本(截至撰写本文时)是 3.18.1。(Github 存储库中标记的版本可以追溯到 3.8.2;2.0.9 版本可以追溯到 2009 年。

到目前为止,我还无法找到哪些版本对应于哪些 Python 版本的完整文档。simplejson