Go json.Decoder More 为什么不能单独判断顶层还有数据
来源:17golang原创
时间:2026-09-27 19:45:45 225浏览 收藏
很多 Go 代码把 dec.More() 当成“顶层输入后面还有没有 JSON”的判断器,结果在读取单个对象、连续 JSON 值和尾随垃圾时得到不同结论。关键点是:More 只回答当前正在解析的数组或对象是否还有成员;它不负责判断顶层流是否结束。
官方文档:https://pkg.go.dev/encoding/json
- 进入数组或对象后,使用
Token读取左括号,再用More控制容器内循环。 - 读取连续的顶层 JSON 值,使用
Decode循环,并把io.EOF当作正常结束。 - 只允许一个顶层值时,首个
Decode后必须再次读取,不能用一次More代替尾随数据检查。
More 只看当前数组或对象的边界
官方定义是“当前数组或对象中是否还有另一个元素”。因此它需要一个容器上下文:在数组里,它判断下一个位置是否已经到 ];在对象里,它判断是否已经到 }。如果解码器还停留在顶层,调用 More 没有“顶层是否还有第二个值”这个语义。
这也是为什么 More 适合配合 Token 使用,而不适合拿来探测整个输入流。图1把解码器、容器栈、当前成员和右括号放在同一张静态结构图中,重点看容器边界,而不是把它理解为文件结束标志。

遍历数组和对象时,先消费 Delim 再调用 More
标准写法是先用 Token 取出左括号,确认它是数组或对象,然后在对应容器内循环。数组元素可以交给 Decode,对象则要先读键,再读值。循环结束后仍要消费右括号,让解码器回到外层。
package main
import (
"encoding/json"
"fmt"
"io"
"strings"
)
func readArray() error {
// 这个示例只在数组容器内使用 More,不把它当作顶层 EOF 判断。
dec := json.NewDecoder(strings.NewReader(`[1,2,3]`))
tok, err := dec.Token()
if err != nil {
return err
}
if delim, ok := tok.(json.Delim); !ok || delim != '[' {
return fmt.Errorf("期望数组起始符,得到 %v", tok)
}
for dec.More() {
var n int
// Decode 消费当前数组元素;错误必须中止当前容器。
if err := dec.Decode(&n); err != nil {
return err
}
fmt.Println(n)
}
_, err = dec.Token() // 消费 ],恢复到数组外层。
return err
}
func main() {
// 真实业务可在这里处理 readArray 的错误;示例省略输出校验。
if err := readArray(); err != nil && err != io.EOF {
panic(err)
}
}
若输入不是数组,应该根据业务返回类型错误,而不是继续调用 More。同理,对象遍历也要在读到 { 后使用 More,并严格按“键、值”成对消费。
顶层连续 JSON 值要用 Decode 和 io.EOF
顶层流可能是 {...}{...} 或多行 JSON 值。这种场景没有一个顶层数组让 More 负责计数,正确的结束信号是下一次 Decode 返回 io.EOF。如果返回的是语法错误或类型错误,应保留错误,不能把它当成“没有更多数据”。
func readJSONStream(r io.Reader) error {
dec := json.NewDecoder(r)
for {
var item map[string]any
// 每次 Decode 消费一个顶层 JSON 值,EOF 才表示流自然结束。
err := dec.Decode(&item)
if err == io.EOF {
return nil
}
if err != nil {
return fmt.Errorf("读取顶层 JSON 失败: %w", err)
}
fmt.Printf("收到对象: %v\n", item)
}
}
解码器会自行缓冲,并且可能从底层 io.Reader 多读一些字节,所以不要用底层 Reader 的一次读取结果推断 JSON 是否结束。图2把“连续顶层值”和“单文档尾随数据检查”分成两个边界,帮助区分正常 EOF、第二个值和坏字节。

只允许一个 JSON 文档时要再读一次
接口接收单个 JSON 对象时,最容易出现的漏洞是只调用一次 Decode,让输入 {"ok":true}{"extra":1} 悄悄通过。首个值解析成功只说明前半段有效,不能说明后面没有第二个值。
单文档策略可以在第一次成功后再调用一次 Decode:返回 io.EOF 才接受;如果读到第二个值或返回语法错误,就拒绝输入。需要允许空白时无需额外处理,因为解码器会跳过 JSON 空白。
| 场景 | 正确判断 | 不要使用 |
|---|---|---|
| 数组/对象内部循环 | Token + More + Decode/Token | 把 More 当成全局 EOF |
| 连续顶层 JSON 值 | Decode 循环,io.EOF 结束 | 用一次 More 探测剩余输入 |
| 单个 JSON 文档 | 首个 Decode 后再次 Decode,必须得到 EOF | Decode 成功一次就直接放行 |
常见疑问
调用 More 返回 false 能说明输入已经结束吗?
不能。它最多说明当前数组或对象没有下一个成员;顶层是否还有值,应继续使用 Decode 或 Token 读取。
为什么不直接用 Token 判断所有结束情况?
Token 适合精确观察括号和基础值,Decode 更适合把完整值交给结构体或映射。选择哪一个取决于你要遍历结构,还是要读取顶层值流。
-
502 收藏
-
502 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
261 收藏
-
330 收藏
-
357 收藏
-
301 收藏
-
389 收藏
-
225 收藏
-
240 收藏
-
497 收藏
-
233 收藏
-
171 收藏
-
269 收藏
-
248 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习