Python 标准库之 json 编码和解码器『详解』

Python 标准库之 json 编码和解码器『详解』_第1张图片

Python 标准库之 json 编码和解码器

文章目录

  • Python 标准库之 json 编码和解码器
    • 一、Python json库介绍
    • 二、导入 json 库
    • 三、Python对应JSON数据类型
    • 四、基本使用「重点」
      • 1、序列化操作
      • 2、反序列化操作
    • 五、编码器和解码器
    • 六、异常
    • 参考资料
    • 相关博客


一、Python json库介绍

JSON(JavaScript Object Notation)是由道格拉斯·克罗克福特构想和设计的一种轻量级资料交换格式。其内容由属性和值所组成,因此也有易于阅读和处理的优势。JSON是独立于编程语言的资料格式,其不仅是JavaScript的子集,也采用了C语言家族的习惯用法,目前也有许多编程语言都能够将其解析和字符串化,其广泛使用的程度也使其成为通用的资料格式。

Python 的 json 库提供了对 json 序列化的支持,与标准库 marshalpickle 相似的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  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  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, intfloatboolNone)字典的键,否则引发一个 TypeError 异常。
  • ensure_ascii:在为 True 时(默认为 True),输出保证将所有输入的非 ASCII 字符转义。如果 ensure_ascii 是 false,这些字符会原样输出。
  • check_circular:检查循环。如果为 False(默认为 True),那么容器类型的循环引用检验会被跳过并且循环引用回引发一个 OverflowError (或者更糟的情况)。
  • allow_nan:数据类型之间的转换遵守。如果为 False(默认为 True),那么在对严格 JSON 规格范围外的 float 类型值(naninf-inf)进行序列化时会引发一个 ValueError 异常。如果为 True,则使用它们的 JavaScript 等价形式(NaNInfinity-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 的默认值会有两种变化,当 indentNone 时,会使用紧凑的一行表达,这时候的分割符会取值 (', ', ': ') 在每个分割符后都有空格,而相反在不为 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(f"类型:{type(x)},内容:{x}"))
    # 使用 object_pairs_hook 参数
    json.loads(s_data, object_pairs_hook=lambda x: print(f"类型:{type(x)},内容:{x}"))
    
    "类型:,内容:{'3': ['第三名', '王汗'], '1': ['第一名', '王小明'], '2': ['第二名', '小明'], '4': ['第四名', '李华'], '5': ['第五名', '李大川'], '6': ['第六名', 'xianzhe_']}"
    "类型:,内容:[('3', ['第三名', '王汗']), ('1', ['第一名', '王小明']), ('2', ['第二名', '小明']), ('4', ['第四名', '李华']), ('5', ['第五名', '李大川']), ('6', ['第六名', 'xianzhe_'])]"
    

    可以看到 object_pairs_hook 并没有返回一个有序列表,而是和 object_hook 一样的顺序,我的猜测是因为程序内部并不知道排序的依据是什么,但其实 object_pairs_hook 传入的参数是列表,那么自己手动指定排序即可

    根据Key键值的排序返回

    # -*- coding: utf-8 -*-
    import json
    
    s_data = '''{
        "3": ["第三名", "王汗"], "1": ["第一名", "王小明"], "2": ["第二名", "小明"],
        "4": ["第四名", "李华"], "5": ["第五名", "李大川"], "6": ["第六名", "xianzhe_"]
    }'''
    
    
    def sort_hook(dic):
        """将Json解码结果排序辅助函数
    
        如果是object_hook参数使用将直接返回
        Args:
            dic: 最基本的类型对象
        """
        if isinstance(dic, list):
            dic.sort(key=lambda i: i[0])
            return dic
        else:
            return dic
    
    
    # 使用 object_hook 参数
    print(json.loads(s_data, object_hook=sort_hook))
    # 使用 object_pairs_hook 参数
    print(json.loads(s_data, object_pairs_hook=sort_hook))
    
    {'3': ['第三名', '王汗'], '1': ['第一名', '王小明'], '2': ['第二名', '小明'], '4': ['第四名', '李华'], '5': ['第五名', '李大川'], '6': ['第六名', 'xianzhe_']}
    [('1', ['第一名', '王小明']), ('2', ['第二名', '小明']), ('3', ['第三名', '王汗']), ('4', ['第四名', '李华']), ('5', ['第五名', '李大川']), ('6', ['第六名', 'xianzhe_'])]
    

    两个参数同时存在的情况下,object_pairs_hook 优先级更高

    # -*- coding: utf-8 -*-
    import json
    
    s_data = '''{
        "3": ["第三名", "王汗"], "1": ["第一名", "王小明"], "2": ["第二名", "小明"],
        "4": ["第四名", "李华"], "5": ["第五名", "李大川"], "6": ["第六名", "xianzhe_"]
    }'''
    
    
    def hook(dic):
        if isinstance(dic, list):
            return "object_pairs_hook优先"
        else:
            return "object_hook优先"
    
    
    print(json.loads(s_data, object_hook=hook, object_pairs_hook=hook))
    
    object_pairs_hook优先
    

2)、json.loads(s, *, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw)

将JSON 编码的 s 字符串对象反序列化为一个 Python 对象。

json.loads() 不同的是,不需要从一个文件对象走获取 JSON 编码数据,而是直接传入一个 JSON 编码的字符串对象,其它参数的含义与 json.loads() 中的相同,因此用法也是一样的,这里不过多讲解。

参数如下:

  • s: 其实就是 str 的缩写,传入需要反 JSON 序列化的字符串对象

注解:如果反序列化的数据不是有效 JSON 文档,引发 JSONDecodeError 错误。


五、编码器和解码器

1)、class json.JSONDecoder(*, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, strict=True, object_pairs_hook=None)

Python 实现 JSON 解码的重要组成部分,一个简单的 JSON 解码器。
在调用反序列化 json.load()json.loads() 方法时,传入的参数大部分实则传给了解码器,这也是参数名一致的原因。
同时将JSON 的 NaNInfinity-Infinity 理解为 Python 对应的 float 值。

源码:

通过查看源码可以看到,JSON 的反序列化方法实则是调用了 JSONDecoder 解码器。

部分源码

	......
    if cls is None:
        cls = JSONDecoder
    if object_hook is not None:
        kw['object_hook'] = object_hook
    if object_pairs_hook is not None:
        kw['object_pairs_hook'] = object_pairs_hook
    if parse_float is not None:
        kw['parse_float'] = parse_float
    if parse_int is not None:
        kw['parse_int'] = parse_int
    if parse_constant is not None:
        kw['parse_constant'] = parse_constant
    return cls(**kw).decode(s)

参数如下:

解码器的参数功能大部分与反序列化 json.load()json.loads() 方法一致。

  • object_hook: 被指定时,其应该是一个函数。它会被调用于每一个解码出的对象字面量(一个 dict、一个基本类型对象),此函数接受一个参数,这个参数就是每一个解码出的对象字面量。
    该函数的返回值会取代原本的 dict,简单来说就是该函数负责把反序列化后的基本类型对象转换成自定义类型的对象。这一特性能够被用于实现自定义解码器(如 JSON-RPC 的类型提示)。
  • parse_float: 解析浮点数。如果指定,将与每个要解码 JSON 浮点数的字符串一同调用。其应该是一个函数,同时接收一个参数,这个参数是每一个 JSON 浮点数的字符串。默认状态下,相当于 float(num_str)
  • parse_int: 解析整数。如果指定,将与每个要解码 JSON 整数的字符串一同调用。其应该是一个函数,同时接收一个参数,这个参数是每一个 JSON 整数的字符串。默认状态下,相当于 int(num_str)
  • parse_constant:解析常量。如果指定,将要与以下字符串中的一个一同调用:'-Infinity''Infinity''NaN'。其应该是一个函数,同时接收一个参数,这个参数是每一个 JSON 整数的字符串。如果遇到无效的 JSON 数字则可以使用它引发异常。
  • strict:严格模式。如果为 False(默认为 True),那么控制字符将被允许在字符串内。在此上下文中的控制字符编码在范围 0–31 内的字符,包括 '\t' (制表符), '\n''\r''\0'
  • object_pairs_hook: 它会被调用于每一个有序列表对解码出的对象字面量,这个参数与 object_hook 参数功能相同,与 object_hook 参数不同点是给 object_pairs_hook 的解码的键值对列表是有序的,一些依赖键值对顺序的功能可以用 object_pairs_hook 参数,而不用 object_hook
    如果 object_hook 也被定义,object_pairs_hook 优先。

方法功能

  • decode(s)
    返回参数 s 的 Python 表示形式,参数 s 是包含一个 JSON 文档的字符串 str 实例。

  • raw_decode(s)
    原始解码数据。从参数 s 中解码出 JSON 文档(以 JSON 文档开头的一个 str 对象)并返回一个 Python 表示形式为 2 元组以及指明该文档在 s 中结束位置的序号。
    这可以用于从一个字符串解码JSON文档,该字符串的末尾可能有无关的数据。

2)、class json.JSONEncoder(*, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, sort_keys=False, indent=None, separators=None, default=None)

Python 实现 JSON 解码的重要组成部分,用于Python数据结构的可扩展JSON编码器。
在调用序列化 json.dump()json.dumps() 方法时,传入的参数大部分实则传给了解码器,这也是参数名一致的原因。

为了将其拓展至识别其他对象,需要子类化并实现 default() 方法于另一种返回 o 的可序列化对象的方法如果可行,否则它应该调用超类实现(来引发 TypeError )。

源码:

通过查看源码可以看到,JSON 的反序列化方法实则是调用了 JSONDecoder 解码器。

部分源码

	......
    # cached encoder
    if (not skipkeys and ensure_ascii and
        check_circular and allow_nan and
        cls is None and indent is None and separators is None and
        default is None and not sort_keys and not kw):
        return _default_encoder.encode(obj)
    if cls is None:
        cls = JSONEncoder
    return cls(
        skipkeys=skipkeys, ensure_ascii=ensure_ascii,
        check_circular=check_circular, allow_nan=allow_nan, indent=indent,
        separators=separators, default=default, sort_keys=sort_keys,
        **kw).encode(obj)

参数如下:

  • skipkeys:在为 True 时(默认为 False)跳过不是基本对象(包括 str, intfloatboolNone)字典的键,否则引发一个 TypeError 异常。
  • ensure_ascii: 在为 True 时(默认为 True),输出保证将所有输入的非 ASCII 字符转义。如果 ensure_ascii 是 false,这些字符会原样输出。
  • check_circular:检查循环。如果为 False(默认为 True),那么容器类型的循环引用检验会被跳过并且循环引用回引发一个 OverflowError (或者更糟的情况)。
  • allow_nan: 数据类型之间的转换遵守。如果为 False(默认为 True),那么在对严格 JSON 规格范围外的 float 类型值(naninf-inf)进行序列化时会引发一个 ValueError 异常。如果为 True,则使用它们的 JavaScript 等价形式(NaNInfinity-Infinity)。
  • sort_keys:在为 True 时(默认为 False)跳过不是基本对象(包括 str, intfloatboolNone)字典的键,否则引发一个 TypeError 异常。
  • indent: 控制缩进内容。
    a、 默认值为 None 会选择最紧凑的表达,即一行数据。
    b、 如果 indent 是一个非负整数或者字符串,那么 JSON 数组元素和对象成员会被美化输出为该值指定的缩进内容,使用一个正整数会让每一层缩进同样数量的空格。
      如果indent是 4 那么将会以4个空格做完每一层的缩进,indent 是一个字符串 (比如 “\t”),那个字符串会被用于缩进每一层。
    c、 如果缩进等级为零、负数或者 "",则只会添加换行符,在每个缩进前都不会有字符。
  • separators: 分割字符,因为用两种分割字符,所以应该是一个 (item_separator, key_separator) 元组(项分割字符,键分割字符)。
      但因为缩进参数 indent 的存在,separators 的默认值会有两种变化,当 indentNone 时,会使用紧凑的一行表达,这时候的分割符会取值 (', ', ': ') 在每个分割符后都有空格,而相反在不为 None 时会取值 (',', ':')
      小技巧:为了得到最紧凑的 JSON 表达式,在 indent 为None 的情况下,可以手动将 separators 取值为 (',', ':'),也就是不留空格。
  • default: 被指定时,其应该是一个函数,每当某个对象无法被序列化时它会被调用,此函数接受一个参数,这个参数就是无法被序列化的对象。
    它应该返回该对象的一个可以被 JSON 编码的版本或者引发一个 TypeError。如果没有被指定,则会直接引发 TypeError

方法功能

  • default(o)
    在子类中实现这种方法使其返回 o 的可序列化对象,或者调用基础实现(引发 TypeError )。

    比如说,为了支持任意迭代器,你可以像这样实现默认设置:

    def default(self, o):
       try:
           iterable = iter(o)
       except TypeError:
           pass
       else:
           return list(iterable)
       # Let the base class default method raise the TypeError
       return json.JSONEncoder.default(self, o)
    encode(o)
    

    返回 Python o 数据结构的 JSON 字符串表达方式。例如:

    >>> json.JSONEncoder().encode({"foo": ["bar", "baz"]})
    '{"foo": ["bar", "baz"]}'
    
  • iterencode(o)
    编码给定对象 o ,并且让每个可用的字符串表达方式。

    for chunk in json.JSONEncoder().iterencode(bigobject):
        mysocket.write(chunk)
    

六、异常

exception json.JSONDecodeError(msg, doc, pos)

参数如下:

  • msg
    未格式化的错误消息
  • doc
    正在解析的 JSON 文档。
  • pos
    解析失败的 doc 的起始索引

源码:

源码比较简单,这里将源码贴出

class JSONDecodeError(ValueError):
    """Subclass of ValueError with the following additional properties:

    msg: The unformatted error message
    doc: The JSON document being parsed
    pos: The start index of doc where parsing failed
    lineno: The line corresponding to pos
    colno: The column corresponding to pos

    """
    # Note that this exception is used from _json
    def __init__(self, msg, doc, pos):
        lineno = doc.count('\n', 0, pos) + 1
        colno = pos - doc.rfind('\n', 0, pos)
        errmsg = '%s: line %d column %d (char %d)' % (msg, lineno, colno, pos)
        ValueError.__init__(self, errmsg)
        self.msg = msg
        self.doc = doc
        self.pos = pos
        self.lineno = lineno
        self.colno = colno

    def __reduce__(self):
        return self.__class__, (self.msg, self.doc, self.pos)

演示代码:

演示手动触发异常,一般也用不上,更多是用于理解源码是如何工作的

# -*- coding: utf-8 -*-
import json

err_json = "{key: {\n'key2':value1}}}key3:value"
raise json.JSONDecodeError("json数据格式错误", err_json, len(err_json))
json.decoder.JSONDecodeError: json数据格式错误: line 2 column 27 (char 34)

参考资料

  • 维基百科中文版:
    • Json
    • 控制字符
  • Python 官方手册:
    • json — JSON 编码和解码器

相关博客

  • Python 牛逼的 pip包管理工具
  • Python 以优雅的姿势 操作文件

你可能感兴趣的:(#,Python,标准库,第三方库,Python,json,python,开发语言)