大家好,我是 SiKi老师。工具循环生成两份关卡配置,每次json.dump都没有报错,为什么整段内容交给json.loads却提示Extra data?先检查输出里究竟是一份 JSON 文档,还是把两份完整文档直接接在了一起。
本文在 Windows、Python 3.12.14 中使用自拟关卡编号和 StringIO,只处理内存文本。没有打开或覆盖真实存档,没有 Unity、Godot 导入、网络传输或并发写入的验证。本题关注多对象输出的文档边界,不重复讨论中文显示、字段默认值或重复字段名。
一 连续调用成功不代表整体可读
最小复现只需要两个对象:
importioimportjson stream=io.StringIO()json.dump({'level':1},stream)json.dump({'level':2},stream)raw=stream.getvalue()print(raw)本地输出为:
{"level": 1}{"level": 2}两次调用各自产生一份对象文本,中间没有包成一个统一的顶层结构。这里没有写磁盘文件,所以“写入成功”只指文本被写到内存流。它不代表存档已经落盘,也不代表后续读取器知道有两份独立文档。
Python 官方文档明确提醒,JSON 不是帧协议,在同一个流上反复 dump 多个对象会产生不合法的 JSON 文件。本文用内存流复现同一个拼接问题,不把这条说明扩展成对所有日志协议的结论。
二 让读取失败可复现
继续对整段文本调用解码:
try:json.loads(raw)exceptjson.JSONDecodeErroraserror:print(error.msg)本地输出Extra data。读取器发现第一份对象之后还有额外数据,没有返回两份对象组成的列表。不要只取前面的对象就宣布恢复成功,否则后面的关卡记录会被忽略。
这份测试特意保留错误原文与完整最小输入,便于区分“后面有额外文档”和其他语法错误。如果真实工具的报错不同,应重新保留脱敏输入并复现,不能看到 JSONDecodeError 就一律认定是连续 dump。
三 需要一份文档时先组织顶层数组
若协议规定这一份输出包含多个关卡对象,可以先组织列表,再序列化一次:
rows=[{'level':1},{'level':2}]text=json.dumps(rows)back=json.loads(text)assertback==rowsprint(len(back))本地断言通过,长度为2。这次输出有一个顶层数组,读取端按同样协议解码,才得到两项。测试还检查了空列表和单项列表的回读;空列表能解析,不代表游戏业务允许没有关卡。
不要未经协商就把现有存档从顶层对象改成数组。读取端若需要对象里的版本号、目录或其他字段,必须按真实协议组织。本文选择数组只是最小示例,不是向所有存档系统推荐同一种结构,也没有实现旧数据迁移。
四 加换行并不会自动变成同一份JSON
独立测试把两份对象中间加上换行,再对整段调用json.loads,仍得到解码异常。给原来的拼接文本末尾再加一个换行,也没有解决问题。换行只是文本字符,不会替读取端选择另一套多记录协议。
与此不同,对一份合法数组文档末尾加换行、空格和制表符,本地仍能正确回读。不要把“允许文档末尾空白”误解为“允许任意多份顶层文档”。两种输入需要分别测试,不能只比较它们都含换行。
有些工具约定一行一个 JSON 值,那是需要生产方和消费方共同实现的另一种协议。本文没有实现逐行读取器,也没有处理字符串内部换行、流式截断、记录大小限制或网络分包。不能只改输出分隔符而不改读取流程。
五 在写真实配置前完成回读验收
独立断言还检查了截断数组会解码失败,以及序列化后自拟列表没有改变。整个验证过程都没有对真实资源产生副作用,保留失败输入也不需要拷贝完整游戏存档。
我会先约定顶层结构,再用空、单项、多项和坏格式分别做回读测试。解析成功后仍要检查字段类型、版本和业务约束;本文只验证边界和这组样例的结果,没有验收关卡内容。资源占用、原子替换与写入失败恢复也不在这段代码里。
如果你的工具也出现 Extra data,可以先描述输出是一次整体生成,还是循环追加,并给出两条脱敏样例。这样能先分清输出协议不一致与普通语法问题,避免用删除后半段数据的方式掩盖错误。
依据:Python 3.12 json 官方文档。示例为原创内存数据,无课程、商业链接或第三方存档内容。