我的Python程序接收JSON数据,我需要从中获取一些信息。如何解析数据并使用结果?我认为我需要使用json。我对这项任务很感兴趣,但我不明白怎么做。

例如,假设我有jsonStr = '{"one": "1", "two": "2", "three": "3"}'。给定这个JSON,和一个输入“2”,我怎么能得到相应的数据“2”?


注意.load是用于文件的;.loads是字符串。请参见:从文件中读取JSON。

有时,JSON文档用于表示表格数据。如果你有类似的东西,并试图与Pandas一起使用它,请参阅Python -如何将JSON文件转换为数据帧。

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

例如,有时数据来自于对原生Python数据结构应用repr。结果可能会使用不同的引号,使用带标题的True和False,而不是json强制的True和False,等等。有关此类数据,请参见将字典的字符串表示形式转换为字典或如何将列表的字符串表示形式转换为列表。

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

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


当前回答

下面是一个简单的例子,可能会对你有所帮助:

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时。dump (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

很容易记住它们的区别:loads后面的s代表“string”。(不可否认,这可能不符合标准的现代命名惯例。)

注意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)

使用请求或其他隐式支持

当使用流行的第三方请求库从Internet检索数据时,不需要从Response对象中提取.text(或创建任何类型的类文件对象)并单独解析它。相反,Response对象直接提供了一个.json方法来进行解析:

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

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

使用结果

默认情况下,通过上述任何方法进行解析都会得到一个非常普通的Python数据结构,由非常普通的内置类型dict、list、str、int、float、bool (JSON true和false成为Python常量true和false)和NoneType (JSON null成为Python常量None)组成。

因此,处理这一结果就像使用其他技术获得相同的数据一样。

因此,我们继续这个问题的例子:

>>> parsed
{'one': '1', 'two': '2', 'three': '3'}
>>> parsed['two']
'2'

我之所以强调这一点,是因为许多人似乎认为这个结果有一些特别之处;没有。它只是一个嵌套的数据结构,尽管处理嵌套有时很难理解。

考虑,例如,解析结果像结果= {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。 使用标准文本编码(通常为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格式使用的转义序列。

在上面的示例中,我在解释器提示符处使用输入来显示示例数据,以避免与转义混淆。下面是一个在源代码中使用字符串文字的类似示例:

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

要使用双引号字符串文字,字符串文字中的双引号也需要转义。因此:

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

Each sequence of \\\" in the input becomes \" in the actual JSON data, which becomes " (embedded within a string) when parsed by the JSON parser. Similarly, \\\\\\\\\\\" (five pairs of backslashes, then an escaped quote) becomes \\\\\" (five backslashes and a quote; equivalently, two pairs of backslashes, then an escaped quote) in the actual JSON data, which becomes \\" (two backslashes and a quote) when parsed by the JSON parser, which becomes \\\\" (two escaped backslashes and a quote) in the string representation of the parsed result (since now, the quote does not need escaping, as Python can use single quotes for the string; but the backslashes still do).

简单的定制

除了strict选项之外,json还提供了关键字选项。加载和json。负载应该是回调的。解析器将调用它们,传入部分数据,并使用返回的数据创建整体结果。

“解析”钩子是不言自明的。例如,我们可以指定将浮点值转换为小数。十进制实例,而不是使用本机Python浮点数:

>>> import decimal
>>> json.loads('123.4', parse_float=decimal.Decimal)
Decimal('123.4')

或者对每个值都使用float,即使它们可以转换为整数:

>>> 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字典。 所提供的object_pairs_hook将使用一个参数调用,该参数是一个键值对列表,否则将用于字典。它应该返回所需的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。

文本编码问题和字节/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值来表示原始数据(str是2.x中字节的别名),并接受unicode值的repr将有一个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版本的全面文档。

非常简单:

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

有时候json不是字符串。例如,如果你从这样的url中得到一个json:

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

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

j_obj = json.load(j)

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

下面是一个简单的例子,可能会对你有所帮助:

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时。dump (data, indent=4)):

{
    "pk": 1, 
    "fb": {
        "new_key": "cc.ee was present!", 
        "fd_id": "12345", 
        "fc": ""
    }, 
    "fa": "cc.ee"
}

对于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']