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

2024-11-20 08:44:00
admin
原创
8
摘要:问题描述:我的 Python 程序接收到 JSON 数据,我需要从中获取一些信息。我该如何解析数据并使用结果?我想我需要使用json.loads这个任务,但我不明白如何做。例如,假设我有jsonStr = '{"one" : "1", "two" :...

问题描述:

我的 Python 程序接收到 JSON 数据,我需要从中获取一些信息。我该如何解析数据并使用结果?我想我需要使用json.loads这个任务,但我不明白如何做。

例如,假设我有jsonStr = '{"one" : "1", "two" : "2", "three" : "3"}'。给定这个 JSON 和的输入"two",我如何获取相应的数据"2"


请注意,这.load是针对文件的;.loads是针对字符串的。另请参阅:从文件读取 JSON。

有时,JSON 文档旨在表示表格数据。如果您有类似的东西并尝试将其与 Pandas 一起使用,请参阅Python - 如何将 JSON 文件转换为 Dataframe。

有些数据表面上看起来像 JSON,但实际上不是 JSON

例如,有时数据来自应用于repr原生 Python 数据结构。结果可能使用不同的引号,使用标题大小写的TrueandFalse而不是 JSON 强制的trueandfalse等。对于此类数据,请参阅将字典的字符串表示形式转换为字典或如何将列表的字符串表示形式转换为列表。

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

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


解决方案 1:

非常简单:

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

解决方案 2:

有时你的 JSON 不是字符串。例如,如果你从这样的 URL 获取 JSON:

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

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

j_obj = json.load(j)

(很容易忘记:'s' 代表 'string')

解决方案 3:

对于 URL 或文件,使用json.load()。对于包含 .json 内容的字符串,使用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']

解决方案 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"
}

解决方案 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,而不是方法.decode。该类的主要优点是它还提供了一种.raw_decode方法,它将忽略 JSON 结束后的额外数据:

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或其他隐含支持

当使用流行的第三方requests库从互联网检索数据时,无需对象中提取.text(或创建任何类型的文件类对象)Response并单独解析它。相反,Response对象直接提供了一个.json执行此解析的方法:

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

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

使用结果

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

因此,使用该结果的方法与使用任何其他技术获取相同数据的方法相同

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

>>> 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 dict,但 JSON 表示的所有其他类型都是允许的)。特别是,它没有在每一行上都有单独的条目 - 这是 JSONL。

  • 使用标准文本编码(通常为 UTF-8)后,数据即可供人类阅读。几乎所有文本都包含在双引号中,并在适当的情况下使用转义序列。

处理嵌入数据

考虑一个包含以下内容的示例文件:

{"one": "{\"two\": \"three\", \"backslash\": \"\\\\\"}"}

这里的反斜杠是JSON 的转义机制。使用上述方法之一进行解析时,我们会得到如下结果:

>>> example = input()
{"one": "{\"two\": \"three\", \"backslash\": \"\\\\\"}"}
>>> parsed = json.loads(example)
>>> parsed
{'one': '{"two": "three", "backslash": "\\\\\"}'}

请注意,这parsed['one']str,而不是dict但实际上,该字符串本身代表“嵌入”的 JSON 数据。

要用解析结果替换嵌入的数据,只需访问数据,使用相同的解析技术,然后从那里继续(例如,通过就地更新原始结果):

>>> parsed['one'] = json.loads(parsed['one'])
>>> parsed
{'one': {'two': 'three', 'backslash': '\\'}}

请注意,'\'此处的部分表示包含一个实际反斜杠的字符串,而不是两个。这遵循了 Python 字符串转义的通常规则,这将我们带到了...

JSON 转义与 Python 字符串文字转义

有时,人们在尝试测试涉及解析 JSON 的代码时会感到困惑,并在 Python 源代码中提供错误的字符串文字作为输入。 尝试测试需要使用嵌入式 JSON 的代码时尤其会发生这种情况。

问题在于 JSON 格式和字符串文字格式各自具有不同的数据转义策略。Python 将处理字符串文字中的转义以创建字符串,而该字符串仍需要包含JSON 格式使用的转义序列。

在上面的例子中,我使用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 可以使用单引号作为字符串;但反斜杠仍然需要)。

简单定制

除了strict选项之外,可用的关键字选项json.load应该json.loads是回调。解析器将调用它们,传入部分数据,并使用返回的内容来创建整体结果。

“解析”钩子相当不言自明。例如,我们可以指定将浮点值转换为decimal.Decimal实例,而不是使用原生 Python 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并且object_pairs_hook可用于控制解析器在给定 JSON 对象时执行的操作,而不是创建 Python 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将使用原本要创建的来调用provided 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 中,bytes支持从对象加载,并将隐式使用 UTF-8 编码:

>>> 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 日起就没有再维护过)应该一致地使用unicode值来表示文本,使用str值来表示原始数据(在 2.x 中str是的别名bytes),并接受值repr会有unicode前缀u(毕竟,代码应该关注值的实际内容,而不是它在 REPL 中的样子)。

历史注释:simplejson

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

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

>>> json.__version__
'2.0.9'

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

我至今还无法找到关于哪个simplejson版本与哪个 Python 版本相对应的全面文档。

解决方案 6:

pathlib.Path对象是处理文件路径的安全、高效且灵活的方法。

因此,这里是使用 pathlib.Path 对象从文件读取 json 的另一种解决方案:

import json
from pathlib import Path

# Create the path object that points to the file which contains json formatted data
json_file_path = Path("mydata.json")

# One-liner for reading the data and converting it to a `dict`
data = json.loads(json_file_path.read_text())

print(data["two"])

免责声明:当使用对象时,此问题及其从文件 重复中读取的内容没有一行解决方案pathlib.Path

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用