JSONDecoder raw_decode 怎样逐段解码
来源:17golang原创
时间:2026-10-25 13:51:58 218浏览 收藏
JSONDecoder.raw_decode 返回“解析出的 Python 值、文档结束索引”这两个结果。保留原字符串,把结束索引作为后续游标,再显式跳过允许的空白,就能逐段读取多个 JSON 值。它只解析当前位置的一个值,不会自动帮你定义多条记录的分隔协议。
我更倾向于先把分隔约定写清楚,再增加循环。本文使用“记录之间至少一个 JSON 空白”的简单约定,输入必须已经完整到达且解码为 str;这是应用配方,不是新的 JSON 文件标准。原创示例未在本机执行。
官方文档:https://docs.python.org/3/library/json.html。CPython 实现参考:https://github.com/python/cpython/blob/3.14/Lib/json/decoder.py。
问题:完整文本里放了多个 JSON 值
设想上游把一个批次对象、一个数组和两个标量写入同一份文本,中间用换行或制表符分隔。对整段调用 json.loads 时,它期望的是一个完整顶层文档,第一段后还有内容就可能出现 Extra data。这个提示不意味着第一段内容必然坏了,也可能只是调用方式与输入约定不一致。
这里不使用 split("}") 或简单正则切分。对象可以嵌套,字符串也可能包含大括号;文档边界应由 JSON 解码器识别。对于上游只有一个数组的情况,则仍然直接 loads 整个数组,无需为了“逐段”把正常文档拆开。
最小配方:值与结束索引一起接收
import json
# 第一个值已经完整,后面仍可有其他数据。
text = '{"batch": "A7"} [2, 4]'
decoder = json.JSONDecoder()
value, end = decoder.raw_decode(text)
# end 是原字符串中的结束位置,不是本次消费的字节数。
print(value)
print(text[end:])
value 是已经转换的 Python 对象,end 则指向第一份文档之后。尾部空白和第二份文档依然属于原字符串。这一配方先展示返回值职责,不把后缀存在误认成解码器已经处理了后缀。

关键游标:显式跳过 JSON 空白
下面是完整的逐段生成器及调用示例。JSON_SPACE 仅列出空格、制表符、回车和换行;不要用更宽泛的空白集合暗中扩大输入协议。raw_decode 的 cursor 指向本轮 JSON 值起点,返回的 end 仍是同一个 text 的绝对字符索引。
import json
JSON_SPACE = " \t\r\n"
def iter_json_values(text):
# 只处理完整文本;记录之间约定至少一个 JSON 空白。
if not isinstance(text, str):
raise TypeError("text 必须是已经解码的字符串")
decoder = json.JSONDecoder()
cursor = 0
while cursor 循环没有使用 text[cursor:] 创建新字符串,因此索引始终相对原文。每轮把 cursor 直接设为 end,而不是 cursor += end。后者会把绝对位置误当成长度,导致后续跳过数据。
这里额外检查 end 后面的字符:如果不是约定的空白就拒绝。例如无分隔的 1true 不会被悄悄当成两条记录。即使对象与数组某些相邻写法可被解码器识别,本配方也只接受约定的空白分隔输入,便于调用双方明确边界。

变体:按对象处理或整批收集
raw_decode 并不限于返回 dict。示例输入按语义应得到四个值:批次对象、整数数组、True 和 None。这是样例解释,不是实测输出。若你的业务只接受对象,应在消费端判断 isinstance(value, dict),然后反馈具体记录不符合业务结构;不要把非对象一概称为 JSON 语法错误。
直接迭代生成器时,前面的记录可能已经交给下游,后面才遇到错误。如果导入必须整批成功,像示例一样先 list 收集,再进入业务处理;若数据量不允许收集全部,应选择事务、暂存或明确的逐条提交策略。解析生成器不会替数据库回滚已提交的数据。
常见坑:字符索引、尾部垃圾与网络分块
| 现象 | 应检查的约定 |
|---|---|
| 开头空白处报错 | 是否先移动 cursor 到值起点 |
| 第二段位置错乱 | end 是否被误当成增量 |
| 中文后错误位置不对 | 是否把字符索引误当成 UTF-8 字节偏移 |
| 尾部任意文字被忽略 | 是否继续解析到只剩允许空白 |
| 半条网络消息被接受 | 是否缺失消息完成标记 |
“逐段解码”不等于“网络增量解析”。JSONDecodeError 并不能可靠区分输入暂时不完整与永久非法;而数字前缀尤其容易误判,例如目前收到 1,后面还可能到达 2,完整记录实际是 12。不要仅凭 raw_decode 成功就提前提交尚未封帧的网络数据。
网络场景应先确定长度、分隔或上层消息完成条件,并正确处理字节到字符的解码,再把完整记录交给解析器。本教程只处理完整 str,不给出猜测式缓存重试器。大文本的内存开销也不会因为使用 yield 就全部消失:完整 text 本身仍在内存中。
完整片段的使用方法和问答
保存完整片段后,用项目支持的 Python 环境运行即可观察类型和值。对业务调用,重点在于把语法解析、记录类型和提交规则分别表达。空字符串或仅含 JSON 空白的文本在本配方中产生零条记录;是否允许空批次由业务决定。
为什么空白需要自己跳过?
raw_decode 从指定位置尝试解码一个值,完整 decode 包装会处理文档前后空白。自行维护位置时,要负责把游标放在值的起点。
可以忽略最后的非 JSON 内容吗?
API 允许你只解码前缀并取得结束索引,但本文的整段记录解析约定不允许尾部垃圾。因此循环会继续读取并报错,不能把第一份值解析成功当作整批有效。
strict=True 就是完全严格的 JSON 吗?
不是。该参数主要控制字符串内控制字符的处理,默认解码器仍有非有限数等扩展行为。如果接口有额外的标准合规要求,要明确对应解析选项和业务约束,不能仅凭这个参数名下结论。
这比按行读 JSON 更合适吗?
取决于上游协议。若上游明确每行一条完整文档,按行处理更直接;若单份对象本身含格式化换行,就不能把每一行当完整 JSON。先确认分隔约定,再选择最小的实现。
-
137 收藏
-
446 收藏
-
337 收藏
-
161 收藏
-
115 收藏
-
254 收藏
-
320 收藏
-
118 收藏
-
196 收藏
-
372 收藏
-
文章 · python教程 | 1小时前 | 异常处理 · python · 异步编程 · asyncio · 超时处理 CancelledError Python异步 asyncio.timeout TimeoutError433 收藏
-
396 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习