pin_drop当前位置:知识文库 ❯ 图文

JSONDecoder详解 - Python自定义反序列化完整指南

JSONDecoder - Python自定义JSON解码器详解

概述

json.JSONDecoderjson 模块中用于 JSON 解码的基类,它定义了将 JSON 字符串反序列化为 Python 对象的默认行为。通过继承 JSONDecoder 并重写 object_hookobject_pairs_hook 方法,可以扩展解码器以支持自定义类型的还原(如将 ISO 日期字符串转回 datetime 对象)。JSONDecoderJSONEncoder 的配套组件,两者配合可实现完整的自定义类型序列化/反序列化循环。


语法

代码示例

import json

class CustomDecoder(json.JSONDecoder):
    def __init__(self, **kwargs):
        kwargs['object_hook'] = self.object_hook
        super().__init__(**kwargs)

    def object_hook(self, obj):
        # 自定义解码逻辑
        return obj

# 使用自定义解码器
data = json.loads(json_str, cls=CustomDecoder)

参数说明

JSONDecoder构造函数

参数 类型 默认值 说明
object_hook callable None 每个JSON object解码后的回调
object_pairs_hook callable None 保留键值对顺序的回调
parse_float callable None 自定义浮点数解析
parse_int callable None 自定义整数解析
parse_constant callable None 自定义NaN/Infinity解析
strict bool True 是否严格检查控制字符

常用方法

方法 说明
decode(s) 解码完整JSON字符串
raw_decode(s) 解码并返回(对象, 结束位置)

返回值

  • decode():返回解析后的 Python 对象

  • raw_decode():返回 (python_object, end_index) 元组


代码示例

示例1:自动还原日期类型

代码示例

import json
from datetime import datetime, date

class DateTimeDecoder(json.JSONDecoder):
    """自动还原datetime和date的解码器"""

    def __init__(self, **kwargs):
        kwargs['object_hook'] = self._hook
        super().__init__(**kwargs)

    def _hook(self, obj):
        for key, value in obj.items():
            if isinstance(value, str):
                # 尝试解析ISO日期时间
                if 'T' in value:
                    try:
                        obj[key] = datetime.fromisoformat(value)
                    except ValueError:
                        pass
                # 尝试解析纯日期
                elif len(value) == 10 and value.count('-') == 2:
                    try:
                        obj[key] = date.fromisoformat(value)
                    except ValueError:
                        pass
        return obj

# 编码
data = {"event": "会议", "time": "2024-01-15T14:30:00", "date": "2024-06-30"}
json_str = json.dumps(data, ensure_ascii=False)

# 解码
result = json.loads(json_str, cls=DateTimeDecoder)
print(f"time类型: {type(result['time']).__name__}")
print(f"time值: {result['time']}")
print(f"date类型: {type(result['date']).__name__}")
print(f"date值: {result['date']}")

输出:

代码示例

time类型: datetime
time值: 2024-01-15 14:30:00
date类型: date
date值: 2024-06-30

示例2:基于类型标记的解码器

代码示例

import json
from datetime import datetime, date
from decimal import Decimal

class TypedDecoder(json.JSONDecoder):
    """基于类型标记的解码器,与UniversalEncoder配套"""

    TYPE_KEYS = {
        '__datetime__': lambda v: datetime.fromisoformat(v),
        '__date__': lambda v: date.fromisoformat(v),
        '__decimal__': lambda v: Decimal(v),
        '__set__': lambda v: set(v),
    }

    def __init__(self, **kwargs):
        kwargs['object_hook'] = self._hook
        super().__init__(**kwargs)

    def _hook(self, obj):
        # 检查是否为类型标记对象
        for type_key, converter in self.TYPE_KEYS.items():
            if type_key in obj and len(obj) == 1:
                return converter(obj[type_key])
        return obj

# 编码+解码完整循环
class TypedEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime):
            return {"__datetime__": obj.isoformat()}
        elif isinstance(obj, date):
            return {"__date__": obj.isoformat()}
        elif isinstance(obj, Decimal):
            return {"__decimal__": str(obj)}
        elif isinstance(obj, set):
            return {"__set__": list(obj)}
        return super().default(obj)

original = {
    "created": datetime(2024, 1, 15, 14, 30),
    "deadline": date(2024, 6, 30),
    "price": Decimal("99.99"),
    "tags": {"python", "json"},
}

# 序列化
json_str = json.dumps(original, cls=TypedEncoder, ensure_ascii=False)
print("JSON:", json_str[:80], "...")

# 反序列化
restored = json.loads(json_str, cls=TypedDecoder)
print(f"\ncreated类型: {type(restored['created']).__name__}")
print(f"price类型: {type(restored['price']).__name__}")
print(f"tags类型: {type(restored['tags']).__name__}")

输出:

代码示例

JSON: {"created": {"__datetime__": "2024-01-15T14:30:00"}, "deadline": {"__date__": "2024-0 ...

created类型: datetime
price类型: Decimal
tags类型: set

示例3:raw_decode处理混合JSON流

代码示例

import json

def parse_json_stream(stream_str):
    """解析包含多个JSON对象的字符串流"""
    decoder = json.JSONDecoder()
    results = []
    idx = 0

    while idx < len(stream_str):
        # 跳过空白
        while idx < len(stream_str) and stream_str[idx].isspace():
            idx += 1

        if idx >= len(stream_str):
            break

        try:
            obj, end = decoder.raw_decode(stream_str, idx)
            results.append(obj)
            idx = end
        except json.JSONDecodeError as e:
            print(f"解析错误位置 {idx}: {e}")
            break

    return results

# 测试:连续的JSON对象
stream = '{"a": 1}  {"b": 2}  {"c": 3}'
results = parse_json_stream(stream)

print(f"解析出 {len(results)} 个对象:")
for i, obj in enumerate(results, 1):
    print(f"  对象{i}: {obj}")

输出:

代码示例

解析出 3 个对象:
  对象1: {'a': 1}
  对象2: {'b': 2}
  对象3: {'c': 3}

实际应用场景

  • 类型还原:与自定义编码器配合,实现特定类型的完整序列化/反序列化循环

  • JSON 流解析:使用 raw_decode() 解析包含多个 JSON 对象的数据流

  • 数据验证:在解码过程中验证数据格式和业务规则


注意事项

注意1object_hook 在每个 JSON object 解码后被调用,嵌套对象从内到外依次触发。外层对象接收到的是已经过 hook 处理的内层对象。

注意2:基于类型标记的解码方式不是 JSON 标准做法,会与其他 JSON 工具不兼容。仅在内部系统间使用。

注意3raw_decode() 不会跳过前导空白,需手动处理。它返回的结束位置是 JSON 对象后的第一个字符位置。

提示object_pairs_hookobject_hook 更强大,它接收键值对列表而非字典,可以检测重复键、保留插入顺序或实现有序字典。


相关方法对比

特性 JSONDecoder子类 object_hook参数 raw_decode 第三方库
可复用性 不适用
类型还原 支持 支持 不适用 支持
流式解析 支持(raw_decode) 不支持 原生 支持
配置复杂度 中等
推荐场景 项目级统一 临时使用 流式数据 高级需求

小结

  • JSONDecoder 通过继承和自定义 object_hook 实现自定义类型的还原

  • 基于类型标记的编解码方案可实现完整的序列化/反序列化循环

  • raw_decode() 可用于解析包含多个 JSON 对象的数据流

  • 类型标记方案不是 JSON 标准,仅在内部系统间使用,对外接口应使用标准 JSON 格式


练习题

练习1

编写一个 StrictDecoder,在解码时检查所有字符串值是否为合法的 UTF-8,对非法编码抛出异常。

练习2

编写一个 DuplicateKeyDecoder,使用 object_pairs_hook 检测 JSON 中的重复键,并报告重复的键名。

练习3

使用 raw_decode() 实现一个 JSONL 文件解析器,逐个解析文件中的 JSON 对象,并统计总数。

常见问题

JSONDecoder和JSONEncoder如何配合使用?

使用类型标记方案:编码器在序列化时用特殊键名(如__datetime__)标记类型,解码器在反序列化时识别这些标记并还原为原始类型,实现完整的序列化/反序列化循环。

object_hook和object_pairs_hook有什么区别?

object_hook接收解码后的字典作为参数;object_pairs_hook接收键值对列表,可以在字典构建前处理数据,支持检测重复键和保留插入顺序,功能更强大。

raw_decode()的使用场景是什么?

raw_decode()用于解析包含多个连续JSON对象的字符串流,每次解析一个对象并返回结束位置,适合处理JSON流式数据或拼接的JSON内容。

为什么类型标记方案不是JSON标准做法?

JSON标准只定义了基本数据类型(字符串、数字、布尔、数组、对象、null),没有类型标记的概念。使用__datetime__等标记会导致与其他JSON工具不兼容,仅适合内部系统。

标签: JSONDecoder object_hook raw_decode 类型还原 JSON流 Python教程

本文涉及AI创作

内容由AI创作,请仔细甄别

list快速访问

上一篇: JSONEncoder详解 - Python自定义JSON序列化完整指南 下一篇: json高级处理详解 - Python JSON流式解析与性能优化指南

poll相关推荐