Python 标准库之 json 编码和解码器『详解』
Posted XianZhe_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python 标准库之 json 编码和解码器『详解』相关的知识,希望对你有一定的参考价值。
Python 标准库之 json 编码和解码器
文章目录
一、Python json库介绍
JSON(javascript Object Notation)是由道格拉斯·克罗克福特构想和设计的一种轻量级资料交换格式。其内容由属性和值所组成,因此也有易于阅读和处理的优势。JSON是独立于编程语言的资料格式,其不仅是JavaScript的子集,也采用了C语言家族的习惯用法,目前也有许多编程语言都能够将其解析和字符串化,其广泛使用的程度也使其成为通用的资料格式。
Python 的 json
库提供了对 json
序列化的支持,与标准库 marshal
和 pickle
相似的API接口。
二、导入 json 库
在看下列内容前,别忘记导入 json 标准库呀
import json
三、Python对应JSON数据类型
1)、JSON 到 Python 数据类型的转换
JSON 数据格式 | To Python 数据格式 |
---|---|
object | dict |
array | list |
string | str |
number (int) | int |
number (real) | float |
true | True |
false | False |
null | None |
2)、Python 到 JSON 数据类型的转换
Python 数据格式 | To JSON 数据格式 |
---|---|
dict | object |
list, tuple | array |
str | string |
int, float, int 和 float 派生的枚举 | number |
True | true |
False | false |
None | null |
3)、源码查阅
在 JSON 标准库的编码器和解码器源码里,能看到对转换类型的注释
JSONEncoder
class JSONEncoder(object):
"""Extensible JSON <http://json.org> encoder for Python data structures.
Supports the following objects and types by default:
+-------------------+---------------+
| Python | JSON |
+===================+===============+
| dict | object |
+-------------------+---------------+
| list, tuple | array |
+-------------------+---------------+
| str | string |
+-------------------+---------------+
| int, float | number |
+-------------------+---------------+
| True | true |
+-------------------+---------------+
| False | false |
+-------------------+---------------+
| None | null |
+-------------------+---------------+
To extend this to recognize other objects, subclass and implement a
``.default()`` method with another method that returns a serializable
object for ``o`` if possible, otherwise it should call the superclass
implementation (to raise ``TypeError``).
"""
pass
JSONDecoder
class JSONDecoder(object):
"""Simple JSON <http://json.org> decoder
Performs the following translations in decoding by default:
+---------------+-------------------+
| JSON | Python |
+===============+===================+
| object | dict |
+---------------+-------------------+
| array | list |
+---------------+-------------------+
| string | str |
+---------------+-------------------+
| number (int) | int |
+---------------+-------------------+
| number (real) | float |
+---------------+-------------------+
| true | True |
+---------------+-------------------+
| false | False |
+---------------+-------------------+
| null | None |
+---------------+-------------------+
It also understands ``NaN``, ``Infinity``, and ``-Infinity`` as
their corresponding ``float`` values, which is outside the JSON spec.
"""
pass
四、基本使用「重点」🧊
1、序列化操作
1)、json.dump(obj, fp, *, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, default=None, sort_keys=False, **kw)
将 obj
对象序列化为 JSON
格式化流形式并存储到 文件,需要注意的是 json 模块始终返回的是 str
字符串对象,因此在将 JSON 读写文件时应以文本模式操作,在序列化操作时应确保 fp.write
支持 str
写入。
参数如下:
- obj: 需要被序列化的对象。
- fp: 传入一个拥有
.write()
写入方法的文件对象,比如file-like
object,需要注意的是应传入一个文本模式的文件对象,并且编码应当是 UTF-8 , UTF-16 或者 UTF-32 。 - skipkeys:在为
True
时(默认为False
)跳过不是基本对象(包括str
,int
、float
、bool
、None
)字典的键,否则引发一个TypeError
异常。 - ensure_ascii:在为
True
时(默认为True
),输出保证将所有输入的非 ASCII 字符转义。如果 ensure_ascii 是 false,这些字符会原样输出。 - check_circular:检查循环。如果为
False
(默认为True
),那么容器类型的循环引用检验会被跳过并且循环引用回引发一个OverflowError
(或者更糟的情况)。 - allow_nan:数据类型之间的转换遵守。如果为
False
(默认为True
),那么在对严格JSON
规格范围外的float
类型值(nan
、inf
和-inf
)进行序列化时会引发一个ValueError
异常。如果为True
,则使用它们的 JavaScript 等价形式(NaN
、Infinity
和-Infinity
)。 - cls:JSON 编码器的类别,默认使用自带的
JSONEncoder
。如果想使用自定义的 JSON 编码器,比如说JSONEncoder
子类,通过此参数实现。 - indent: 控制缩进内容。
a、 默认值为None
会选择最紧凑的表达,即一行数据。
b、 如果 indent 是一个非负整数或者字符串,那么 JSON 数组元素和对象成员会被美化输出为该值指定的缩进内容,使用一个正整数会让每一层缩进同样数量的空格。
如果indent是 4 那么将会以4个空格做完每一层的缩进,indent 是一个字符串 (比如 “\\t”),那个字符串会被用于缩进每一层。
c、 如果缩进等级为零、负数或者""
,则只会添加换行符,在每个缩进前都不会有字符。 - separators: 分割字符,因为用两种分割字符,所以应该是一个
(item_separator, key_separator)
元组(项分割字符,键分割字符)。
但因为缩进参数indent
的存在,separators
的默认值会有两种变化,当indent
为None
时,会使用紧凑的一行表达,这时候的分割符会取值(', ', ': ')
在每个分割符后都有空格,而相反在不为None
时会取值(',', ':')
。
小技巧:为了得到最紧凑的 JSON 表达式,在 indent 为None
的情况下,可以手动将 separators 取值为(',', ':')
,也就是不留空格。 - default: 被指定时,其应该是一个函数,每当某个对象无法被序列化时它会被调用,此函数接受一个参数,这个参数就是无法被序列化的对象。
它应该返回该对象的一个可以被 JSON 编码的版本或者引发一个TypeError
。如果没有被指定,则会直接引发TypeError
。 - sort_keys:如果为
True
(默认为 False),那么字典的输出会以键的顺序排序。
具体讲解:
-
取消非ASCII编码转义
在将 JSON 编码的数据存储到文件或是进行编码返回时,很容易发现除了ASCII字符都发生了转义,变成了Unicode的转义字符,最典型的例子就是中文字符。
# -*- coding: utf-8 -*- import json from pathlib import Path BASE_DIR = Path(__file__).parent happly_new_year = "年份": 2022, "生肖": "虎", "祝福语": "在这美丽的春节之际,祝福各位朋友在新的一年里,天天都有份好心情!", "春节快乐": None # 文件路径 file_path = BASE_DIR / "test.json" # 打印输出到控制台 print(json.dumps(happly_new_year)) # 输出存储到文件 with file_path.open("w", encoding="utf-8") as f_w: json.dump(happly_new_year, fp=f_w)
控制台输出 与 输出到文件
"\\u5e74\\u4efd": 2022, "\\u751f\\u8096": "\\u864e", "\\u795d\\u798f\\u8bed": "\\u5728\\u8fd9\\u7f8e\\u4e3d\\u7684\\u6625\\u8282\\u4e4b\\u9645\\uff0c\\u795d\\u798f\\u5404\\u4f4d\\u670b\\u53cb\\u5728\\u65b0\\u7684\\u4e00\\u5e74\\u91cc\\uff0c\\u5929\\u5929\\u90fd\\u6709\\u4efd\\u597d\\u5fc3\\u60c5!", "\\u6625\\u8282\\u5feb\\u4e50": null
那么对其进行 JSON 编码时如何取消发生的非 ASCII 字符转义,让这些字符会原样输出呢?答案很简单,在每次编码时将
ensure_ascii
参数设置为Flase
即可。上部分的代码保持不变,只是添加
ensure_ascii
参数...... # 打印输出到控制台 print(json.dumps(happly_new_year, ensure_ascii=False)) # 输出存储到文件 with file_path.open("w", encoding="utf-8") as f_w: json.dump(happly_new_year, fp=f_w, ensure_ascii=False)
控制台输出 与 输出到文件
"年份": 2022, "生肖": "虎", "祝福语": "在这美丽的春节之际,祝福各位朋友在新的一年里,天天都有份好心情!", "春节快乐": null
-
JSON 数据格式化
每次输出的 JSON 数据都是一行显示的,在数据量比较少的情况下还能阅读,当数据量一旦多起来后阅读体验上就没那么好了。JSON 本身就是一种注重数据结构化的格式,在维护调试时可以充分发挥其优点,对其进行格式化就是一种常见手段。
将其进行格式化返回需要使用到indent
参数,indent
参数的作用在参数讲解部分有将,这边咱们对这个参数作用进行展示。以四个空格为缩进
# -*- coding: utf-8 -*- import json from pathlib import Path BASE_DIR = Path(__file__).parent happly_new_year = "年份": 2022, "生肖": "虎", "祝福语": "在这美丽的春节之际,祝福各位朋友在新的一年里,天天都有份好心情!", "春节快乐": None # 文件路径 file_path = BASE_DIR / "test.json" # 打印输出到控制台 print(json.dumps(happly_new_year, indent=4, ensure_ascii=False)) # 输出存储到文件 with file_path.open("w", encoding="utf-8") as f_w: json.dump(happly_new_year, fp=f_w, indent=4, ensure_ascii=False)
"年份": 2022, "生肖": "虎", "祝福语": "在这美丽的春节之际,祝福各位朋友在新的一年里,天天都有份好心情!", "春节快乐": null
以两个空格为缩进
...... # 打印输出到控制台 print(json.dumps(happly_new_year, indent=2, ensure_ascii=False)) # 输出存储到文件 with file_path.open("w", encoding="utf-8") as f_w: json.dump(happly_new_year, fp=f_w, indent=2, ensure_ascii=False)
"年份": 2022, "生肖": "虎", "祝福语": "在这美丽的春节之际,祝福各位朋友在新的一年里,天天都有份好心情!", "春节快乐": null
-
default 参数的妙用
default 其实是一个很方便的参数,向这个参数传入一个处理自定义对象的函数,就能够将这些自定义对象转义成可以被 JSON 编码的版本,而不需要在将数据序列化前还要手动转义 JSON 编码器无法解析的数据源。
有一个水果类,为每一种水果都新建一个对象,每个对象中都有一个方法可以将其转义成可以被 JSON 编码版本的方法,需要做的是在每次序列化时都能正确调用这个方法将其转义。这时 default 这个参数就派上用场了,传入 default 的函数要做两件事,如果是水果类的对象那就将其转义,如果不是则抛出异常。
# -*- coding: utf-8 -*- import json from pathlib import Path BASE_DIR = Path(__file__).parent class Fruits: """水果类""" def __init__(self, name, price): self.name = name self.price = price def __str__(self): return f"水果类型:self.name,售价:self.price" def todict(self): """将水果信息转换成字典格式输出""" return "水果": self.name, "售价": self.price def obj_to_json(obj): """对象转为JSON能编码函数 Args: obj: 自定义对象 Returns: 能够被JSON编码的版本数据 Raises: 如果不是目标类别的对象将抛出TypeError异常 """ if isinstance(obj, Fruits): return obj.todict() raise TypeError(f"Object of type obj.__name__ is not JSON serializable") pear = Fruits("梨子", 3.3) apple = Fruits("苹果", 5.6) banana = Fruits("香蕉", 11.6) orange = Fruits("橙子", 6.6) fruits = [pear, apple, banana, orange] # 文件路径 file_path = BASE_DIR / "test.json" with file_path.open("w", encoding="utf-8") as f_w: json.dump(fruits, fp=f_w, default=obj_to_json, ensure_ascii=False)
JSON 数据
["水果": "梨子", "售价": 3.3, "水果": "苹果", "售价": 5.6, "水果": "香蕉", "售价": 11.6, "水果": "橙子", "售价": 6.6]
2)、json.dumps(obj, *, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, default=None, sort_keys=False, **kw)
将 obj
序列化为 JSON 格式的 str
对象并返回,不会进行文件式存储
除了缺少用于存储到文件的 fp
参数外,其它参数的含义与 json.dump()
中的相同,因此用法也是一样的,只不过一个是存储到文件,一个是直接返回 JSON 编码数据,这里不过多讲解
注解: JSON 中的键-值对中的键永远是 str 类型的。当一个对象被转化为 JSON 时,字典中所有的键都会被强制转换为字符串。这所造成的结果是字典被转换为 JSON 然后转换回字典时可能和原来的不相等。换句话说,如果 x 具有非字符串的键,则有 loads(dumps(x)) != x。比如说在Python数据类型中以整数为键,转化为 JSON 时这些整数将会变成字符串类型。
2、反序列化操作
1)、json.load(fp, *, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw)
将 fp
(一个支持 .read()
并包含一个 JSON 文档的 text file
或者 binary file
) 反序列化为一个 Python 对象。
参数如下:
- fp: 传入一个拥有
.read()
写入方法的文件对象,该文件对象应是text file
(文本对象)或binary file
(二进制对象),对于二进制对象输入编码应当是 UTF-8 , UTF-16 或者 UTF-32。 - cls:JSON 解码器的类别,默认使用自带的 JSONDecoder。如果想使用自定义的 JSON 解码器,比如说 JSONDecoder 子类,通过此参数实现。额外的关键词参数会通过类的构造函数传递。
- object_hook: 被指定时,其应该是一个函数。它会被调用于每一个解码出的对象字面量(一个
dict
、一个基本类型对象),此函数接受一个参数,这个参数就是每一个解码出的对象字面量。
该函数的返回值会取代原本的dict
,简单来说就是该函数负责把反序列化后的基本类型对象转换成自定义类型的对象。这一特性能够被用于实现自定义解码器(如 JSON-RPC 的类型提示)。 - parse_float:解析浮点数。如果指定,将与每个要解码 JSON 浮点数的字符串一同调用。其应该是一个函数,同时接收一个参数,这个参数是每一个 JSON 浮点数的字符串。默认状态下,相当于
float(num_str)
。
可以用于对 JSON 浮点数使用其它数据类型和语法分析程序 (比如decimal.Decimal
)。 - parse_int:解析整数。如果指定,将与每个要解码 JSON 整数的字符串一同调用。其应该是一个函数,同时接收一个参数,这个参数是每一个 JSON 整数的字符串。默认状态下,相当于
int(num_str)
。
可以用于对 JSON 整数使用其它数据类型和语法分析程序 (比如float
)。 - parse_constant:解析常量。如果指定,将要与以下字符串中的一个一同调用:
'-Infinity'
,'Infinity'
,'NaN'
。其应该是一个函数,同时接收一个参数,这个参数是每一个 JSON 整数的字符串。如果遇到无效的 JSON 数字则可以使用它引发异常。 - object_pairs_hook: 它会被调用于每一个有序列表对解码出的对象字面量,这个参数与
object_hook
参数功能相同,与object_hook
参数不同点是给object_pairs_hook
的解码的键值对列表是有序的,一些依赖键值对顺序的功能可以用object_pairs_hook
参数,而不用object_hook
。
如果object_hook
也被定义,object_pairs_hook
优先。
具体讲解:
-
JSON 浮点数处理
parse_float
参数用于控制 JSON 浮点数转化为 Python 数据类型时的操作,默认是对其转化为浮点数类型float(num_str)
,浮点数还能做的处理,比如四舍五入,向上向下取整,转化为decimal.Decimal
等,这些都能直接靠parse_float
参数实现。# -*- coding: utf-8 -*- import math import json from pathlib import Path from decimal import Decimal BASE_DIR = Path(__file__).parent # 文件路径 file_path = BASE_DIR / "test.json" def json_ceil(dic): """将json浮点数向上取整辅助函数""" return math.ceil(float(dic)) def json_floor(dic): """将json浮点数向下取整辅助函数""" return math.floor(float(dic)) def json_round(dic): """将json浮点数四舍五入取整辅助函数""" return round(float(dic)) def float_to_decimal(dic): """将json浮点数转化Decimal辅助函数""" return Decimal.from_float(float(dic)) .......
源 JSON 数据
["水果": "梨子", "售价": 3.3, "水果": "苹果", "售价": 5.6, "水果": "香蕉", "售价": 11.6, "水果": "橙子", "售价": 6.6]
向上取整
...... with file_path.open("r", encoding="utf-8") as f_r: print(json.load(f_r, parse_float=json_ceil))
['水果': '梨子', '售价': 4, '水果': '苹果', '售价': 6, '水果': '香蕉', '售价': 12, '水果': '橙子', '售价': 7]
向下取整
...... with file_path.open("r", encoding="utf-8") as f_r: print(json.load(f_r, parse_float=json_floor))
['水果': '梨子', '售价': 3, '水果': '苹果', '售价': 5, '水果': '香蕉', '售价': 11, '水果': '橙子', '售价': 6]
四舍五入取整
...... with file_path.open("r", encoding="utf-8") as f_r: print(json.load(f_r, parse_float=json_round))
['水果': '梨子', '售价': 3, '水果': '苹果', '售价': 6, '水果': '香蕉', '售价': 12, '水果': '橙子', '售价': 7]
转化为Decimal类型
...... with file_path.open("r", encoding="utf-8") as f_r: print(json.load(f_r, parse_float=float_to_decimal))
['水果': '梨子', '售价': Decimal('3.29999999999999982236431605997495353221893310546875'), '水果': '苹果', '售价': Decimal('5.5999999999999996447286321199499070644378662109375'), '水果': '香蕉', '售价': Decimal('11.5999999999999996447286321199499070644378662109375'), '水果': '橙子', '售价': Decimal('6.5999999999999996447286321199499070644378662109375')]
-
JSON 整数处理
在解析 JSON 整数字符串时,默认是将其转化为整数类型int(num_str)
,可以将其转化为浮点数类型,直接靠parse_float
参数实现。# -*- coding: utf-8 -*- import math import json from pathlib import Path from decimal import Decimal BASE_DIR = Path(__file__).parent file_path = BASE_DIR / "test.json" with file_path.open("r", encoding="utf-8") as f_r: print(json.load(f_r, parse_int=float))
源 JSON 数据
"祝福语": "新春快乐!", "祝你": 1314
程序打印
'祝福语': '新春快乐!', '祝你': 1314.0
-
object_hook 与 object_pairs_hook 的应用
object_hook
参数与object_pairs_hook
参数功能都是一致的,但在编写函数时,函数接受的参数不同。对于object_hook
参数,传入的是每一个解码出来最基本的类型对象,即每次传入都是dict
字典数据类型。对于object_pairs_hook
参数,传入的是每一个解码出来的列表嵌套元组类型[(key, value), ]
,相当于调用了字典的.items()
方法。颁奖排序算是一个典型例子,以数字1、2、3…为颁奖奖次的依据,分别使用
object_hook
参数与object_pairs_hook
参数进行示例。# -*- coding: utf-8 -*- import json s_data = ''' "3": ["第三名", "王汗"], "1": ["第一名", "王小明"], "2": ["第二名", "小明"], "4": ["第四名", "李华"], "5": ["第五名", "李大川"], "6": ["第六名", "xianzhe_"] ''' # 使用 object_hook 参数 json.loads(s_data, object_hook=lambda x: print(以上是关于Python 标准库之 json 编码和解码器『详解』的主要内容,如果未能解决你的问题,请参考以下文章