当前位置:首页 > 文章列表 > Golang > Go问答 > Go json.Decoder More 为什么不能单独判断顶层还有数据

Go json.Decoder More 为什么不能单独判断顶层还有数据

来源:17golang原创 2026-09-27 19:45:45 0浏览 收藏

很多 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把解码器、容器栈、当前成员和右括号放在同一张静态结构图中,重点看容器边界,而不是把它理解为文件结束标志。

Go json.Decoder More 只在数组或对象容器内判断成员边界的结构说明图
图1:More 的判断范围停留在当前数组或对象容器内,这是静态结构说明图,不是运行截图。

遍历数组和对象时,先消费 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、第二个值和坏字节。

Go 顶层 JSON 流使用 Decode 和 io.EOF 判断结束的关系说明图
图2:顶层流由 Decode 与 io.EOF 管理,More 不承担顶层结束判断;这是静态关系说明图。

只允许一个 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,必须得到 EOFDecode 成功一次就直接放行

常见疑问

调用 More 返回 false 能说明输入已经结束吗?

不能。它最多说明当前数组或对象没有下一个成员;顶层是否还有值,应继续使用 Decode 或 Token 读取。

为什么不直接用 Token 判断所有结束情况?

Token 适合精确观察括号和基础值,Decode 更适合把完整值交给结构体或映射。选择哪一个取决于你要遍历结构,还是要读取顶层值流。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
photocolors怎么导出色盘和记忆卡片?调整、预览与分享说明photocolors怎么导出色盘和记忆卡片?调整、预览与分享说明
上一篇
photocolors怎么导出色盘和记忆卡片?调整、预览与分享说明
PHP Randomizer 怎么实现可替换的随机引擎
下一篇
PHP Randomizer 怎么实现可替换的随机引擎
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    239次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    287次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    254次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    236次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    45次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码