当前位置:首页 > 文章列表 > Golang > Go问答 > Go json.Decoder Decode 成功后为什么还要检查尾随内容

Go json.Decoder Decode 成功后为什么还要检查尾随内容

来源:17golang原创 2026-10-05 19:13:04 0浏览 收藏

json.Decoder.Decode 返回 nil,只表示它成功读取了输入中的下一个 JSON 值,并不表示整个输入流已经结束。对于 HTTP 请求体、配置文件等“只能提交一个顶层 JSON 值”的接口,第一次解码后还要再调用一次 Decode:只有第二次得到 io.EOF,才能确认后面只剩允许的空白字符。

官方文档:https://pkg.go.dev/encoding/json

Decode 的目标是读取下一个值

Decoder 面向 io.Reader,本来就支持从流里连续读取多个 JSON 值。输入是 {"id":1} {"id":2} 时,第一次 Decode 读取第一个对象后即可成功返回;第二个对象仍留在流中,等待下一次调用。这个行为适合日志流或协议明确允许连续值的场景,却不等于“整个请求体是一个完整且唯一的 JSON”。

第一次 Decode 与尾随检查区分三类 JSON 输入的说明图
图1:第一次 Decode 只覆盖首个完整值;尾随检查决定空白结尾是否可接受,以及额外值或非法字节是否应拒绝。

因此要先写清接口契约:如果输入允许连续 JSON 值,就循环调用 Decode;如果输入必须恰好一个值,就必须确认首个值之后到达 EOF。

三类尾部结果应该怎样判断

首个 JSON 后面的内容第二次 Decode结论
空格、换行、制表符后结束io.EOF接受,输入只有一个 JSON 值
另一个合法 JSON 值nil拒绝,输入包含多个值
无法构成 JSON 的字节语法错误等非 EOF 错误拒绝,存在非法尾随内容

关键不是把第二个值保存下来,而是判断第二次调用的错误是否恰好为 io.EOF。把任意错误都当作“已经结束”会错误地放过垃圾字节;只检查第一次错误则会放过完整的第二个 JSON 值。

封装一个只接受单个 JSON 值的入口

下面的辅助函数把“业务对象解码”和“输入是否结束”分成两个判断。空结构体仅作为第二次读取的接收目标;无论尾部是对象、数组、字符串、数字还是 null,只要结果不是 io.EOF,都按尾随内容处理。

package strictjson

import (
    "encoding/json"
    "errors"
    "fmt"
    "io"
)

func DecodeExactlyOne(r io.Reader, dst any) error {
    dec := json.NewDecoder(r)

    // 可选:业务结构体不接受未声明字段时开启;它不替代尾随检查。
    dec.DisallowUnknownFields()

    // 第一次调用只负责读取并转换首个 JSON 值。
    if err := dec.Decode(dst); err != nil {
        return fmt.Errorf("解析 JSON:%w", err)
    }

    // 第二次调用只探测是否还有内容;只有 EOF 表示后面仅剩空白。
    var extra struct{}
    err := dec.Decode(&extra)
    if errors.Is(err, io.EOF) {
        return nil
    }
    if err == nil {
        return errors.New("输入包含多个 JSON 值")
    }
    return fmt.Errorf("JSON 后存在非法尾随内容:%w", err)
}

这里使用 errors.Is(err, io.EOF) 明确表达结束条件。第二次调用返回 nil,说明确实又读到了一个 JSON 值;返回其他错误,说明尾部存在无法按 JSON 继续解析的内容。两种情况都违反“恰好一个值”的契约。

io.Reader 经过 json.Decoder 解码业务结构体并进行第二次 Decode 的结构图
图2:未知字段和数字表示属于解码配置,第二次 Decode 则单独负责确认输入流已经结束。

不要用 More 或 Buffered 代替结束检查

Decoder.More 用来判断当前数组或对象中是否还有元素,不是判断顶层输入流是否结束。对顶层单值请求直接使用它,会混淆容器边界和流边界。

Decoder.Buffered 只返回 Decoder 已经预读但尚未使用的那一部分数据。底层 io.Reader 可能仍有更多内容,因此仅查看缓冲区是否为空也不能证明已经到达输入末尾。让 Decoder 再读取一次,才能把内部缓冲和底层 Reader 一起纳入判断。

严格字段与尾随内容是两种约束

DisallowUnknownFields 解决的是“首个对象里是否出现目标结构体没有的字段”;UseNumber 影响数字解码到接口值时的表示。它们都不会自动把输入限制为一个顶层值。调用方需要分别决定字段策略、数字策略和流结束策略,不能因为首个对象严格解码成功,就省略 EOF 检查。

需求对应做法
拒绝未知对象字段DisallowUnknownFields
保留接口值中的数字字面量UseNumber
只允许一个顶层 JSON 值第二次 Decode 必须得到 io.EOF
允许连续 JSON 流循环 Decode,直到 io.EOF

常见问题

第二次 Decode 会不会把结尾空白当成错误?

不会。JSON 值后的合法空白会被跳过,输入结束时返回 io.EOF,这正是单值契约应接受的结果。

为什么不用 json.Valid 检查整个请求体?

json.Valid 接收完整字节切片,适合数据已经全部在内存中的情况。面对 io.Reader,Decoder 可以直接流式读取;但调用方要补上第二次 Decode,明确验证“恰好一个值”。

什么时候不应该拒绝第二个 JSON 值?

当协议本身定义为连续 JSON 值流时,第二个值是合法数据,应循环解码直到 EOF。是否检查尾随内容不是 Decoder 的统一开关,而是由你的输入协议决定。

结论很简单:第一次 Decode 回答“能否读取下一个 JSON 值”,第二次调用回答“后面是否真的结束”。只有这两个条件同时成立,才能把输入当作恰好一个完整 JSON 值。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
诗歌本的 Google Play 下载量怎么看?500+标识与产品页字段说明诗歌本的 Google Play 下载量怎么看?500+标识与产品页字段说明
上一篇
诗歌本的 Google Play 下载量怎么看?500+标识与产品页字段说明
LT画质助手支持144/165帧吗?和平精英高帧率与硬件边界说明
下一篇
LT画质助手支持144/165帧吗?和平精英高帧率与硬件边界说明
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之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模型性能。
    343次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    403次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    400次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    362次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    183次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码