当前位置:首页 > 文章列表 > Golang > Go问答 > Go json.Decoder 连续 JSON 怎么读:Decode 循环、EOF 与尾部数据校验

Go json.Decoder 连续 JSON 怎么读:Decode 循环、EOF 与尾部数据校验

来源:17golang原创 2026-07-27 11:08:01 0浏览 收藏
所属专题:Go JSON 工程实践专题 - 从标准库解码、流式 JSON 到 json/v2 迁移与接口治理

日志采集接口收到的经常不是单个JSON,而是好几个紧挨着拼接在一起的对象:{"id":1}{"id":2}。这时候如果直接用 json.Unmarshal 去解析,会因为尾部还剩未处理的内容直接报错;要是把整个请求体一次性读到内存里手动切分,又很容易把字符串字段里藏的大括号误当成两个对象的分界点。更稳妥的方案是直接交给 json.Decoder 处理,每次读取一个完整的JSON值,直到真的读到 io.EOF 为止。

要点速览

  • json.Decoder.Decode 每次消费一个 JSON 值,值之间可以只有空白字符,不要求换行。
  • 循环结束时的 io.EOF 是正常收尾;语法错误、类型错误和超长请求体要分别处理。
  • 若接口只允许“一个或多个 JSON 值”,读完后还要确认没有第二个非空值,不能只看第一次成功。
  • HTTP 请求体应先套 http.MaxBytesReader,并在所有路径关闭 r.Body

先确认输入到底是单个 JSON 还是 JSON 流

这两个格式看起来只差一个额外的对象,但对应的接口契约完全不同。单个 JSON 通常长这样:

{"id":1,"event":"login"}

连续 JSON 则可能来自日志管道、批量推送或长连接无分隔帧场景:

{"id":1,"event":"login"}
{"id":2,"event":"logout"}

如果服务端只允许一个对象,收到第二个对象应报错;如果接口本来就是流式协议,才应该继续调用 Decode。这里先定清边界,后面的 EOF 判断才不会把脏数据当成合法输入结束。

Go json.Decoder 从 HTTP 请求体读取连续 JSON:输入、Decode 单值、io.EOF 收尾的流程

用 Decode 循环按完整值推进读取位置

下面的示例把每个 JSON 对象放进同一个结构体。Decoder 会维护内部缓冲,不需要自己查找大括号,也不会因为字符串字段里出现 {} 就提前截断。

type Event struct {
    ID    int    `json:"id"`
    Event string `json:"event"`
}

func readEvents(r io.Reader) ([]Event, error) {
    dec := json.NewDecoder(r)
    var events []Event
    for {
        var item Event
        err := dec.Decode(&item)
        if errors.Is(err, io.EOF) {
            return events, nil
        }
        if err != nil {
            return nil, fmt.Errorf("decode event: %w", err)
        }
        events = append(events, item)
    }
}

这个函数的关键不是“循环”,而是错误分层:只有 io.EOF 表示输入正常结束。空白、换行和多个值之间的缩进都会被解码器跳过;半截对象则会返回语法错误,调用方不应把已经追加的结果直接当成完整批次。

类型不对时,为什么不能只检查 EOF

假设输入里的 id 变成字符串,Decode 可能返回类型错误。即使某些字段已经写进 item,这个对象也不应加入结果集。示例中只有没有错误时才 append,正是为了避免半成品混入后续处理。

单值接口要额外检查第二次 Decode

很多“解析成功但接口仍不安全”的问题,都出在只调用了一次 Decode。如果契约是一个 JSON 对象,第二次读取应当只能得到 io.EOF

func readOne(r io.Reader) (Event, error) {
    dec := json.NewDecoder(r)
    var item Event
    if err := dec.Decode(&item); err != nil {
        return Event{}, fmt.Errorf("first value: %w", err)
    }

    var extra json.RawMessage
    if err := dec.Decode(&extra); !errors.Is(err, io.EOF) {
        if err == nil {
            return Event{}, errors.New("more than one JSON value")
        }
        return Event{}, fmt.Errorf("trailing data: %w", err)
    }
    return item, nil
}

第二个 JSON 值前即使有换行和空格,也会被跳过,所以这种写法不会把格式化空白误报成脏数据。若协议允许注释、逗号或自定义分隔符,则不能把它们直接当成标准 JSON 输入,应在协议层先作明确约定。

Go 单值接口第二次 Decode 校验尾部:空白正常结束、第二个 JSON 值和语法错误分别分流

HTTP 请求体还要加上大小和关闭边界

流式读取不等于无限读取。对外接口可以先把请求体限制在业务允许的大小,再交给解码函数:

func eventHandler(w http.ResponseWriter, r *http.Request) {
    defer r.Body.Close()
    limited := http.MaxBytesReader(w, r.Body, 1

MaxBytesReader 把超限变成可识别的读取错误,避免客户端用一个永不结束的大请求拖住处理协程。实际项目里还应配合请求超时、Content-Type 检查和每批事件数量上限;这些是不同的边界,不能只靠 JSON 语法校验代替。

现象更可能的含义处理动作
第一次 Decode 立即 EOF请求体为空按业务返回缺少输入
第一次成功,第二次成功单值接口收到多个值拒绝尾部数据,检查调用方
返回 unexpected EOFJSON 在传输中被截断记录请求大小和连接超时
读取超出限制请求体超过 1 MiB返回 413 或业务约定的错误

常见问题:Decode 循环和 EOF

JSON 值之间必须换行吗?

不必须。标准 JSON 空白包括空格、换行、回车和制表符,两个完整值只要能被解码器区分即可。

为什么不用 strings.Split 切 JSON?

字符串值、转义字符和嵌套对象都可能包含看似边界的字符,按文本切分无法可靠判断 JSON 结构。应使用解码器或上游提供明确的行协议。

读到 EOF 后还能复用同一个 Decoder 吗?

通常把 EOF 视为输入结束即可,不要继续等待同一个已结束的有限 Reader。长连接场景则应由外层协议决定下一帧何时到达。

已经解析出前几个对象,后面出错能否继续入库?

这取决于批处理是否允许部分成功。订单、账务等场景更适合先落临时批次,全部通过后再提交;日志类场景可以记录失败位置后继续,但必须让调用方知道结果不完整。

把 EOF 当作协议状态,而不是异常字符串

连续 JSON 的可靠读取,核心是让输入格式、解码进度和业务提交边界互相对应:流式协议循环消费,每次成功只提交一个完整值;单值接口再读一次确认尾部;HTTP 层补上大小、超时和关闭。这样日志里的 EOF、截断和多值就各自有清晰含义,排查时也不必靠猜。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Go 构建 AI 评测样本 CLI:校验 JSONL、抽样回放并生成通过率报告Go 构建 AI 评测样本 CLI:校验 JSONL、抽样回放并生成通过率报告
上一篇
Go 构建 AI 评测样本 CLI:校验 JSONL、抽样回放并生成通过率报告
Go HTTP PATCH 怎么区分字段缺失和显式置空:指针字段、null 语义与兼容返回
下一篇
Go HTTP PATCH 怎么区分字段缺失和显式置空:指针字段、null 语义与兼容返回
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之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推荐
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    97次使用
  • OpenCompass大模型评测体系详解:功能、使用指南与应用场景
    OpenCompass
    OpenCompass是上海AI实验室推出的开源大模型评测平台,提供CompassKit、CompassHub和CompassRank三大核心组件,支持LLM及多模态模型的一站式标准化评估与排行榜查询。
    28次使用
  • SuperCLUE中文大模型评测基准:功能、能力维度与应用指南
    SuperCLUE
    SuperCLUE是权威的中文大语言模型综合评测基准,涵盖语言理解、知识应用、AI Agent智能体及安全性等12项核心能力。通过多轮对话与客观测试,定期发布榜单与技术报告,为模型研发、优化及行业选型提供科学依据。
    252次使用
  • C-Eval中文评测基准:大语言模型多学科能力评估指南
    C-Eval
    深入了解C-Eval中文评估套件,涵盖52个学科与4级难度。本文详解其功能特点、Zero-shot/Few-shot使用方法及代码示例,助您全面评测LLM中文理解与泛化能力。
    180次使用
  • AI Prompt Library:免费AI提示词库,助力ChatGPT高效创作与营销
    AI Prompt Library
    探索AI Prompt Library免费资源库,涵盖营销、写作及多场景AI提示词。兼容ChatGPT、Claude等工具,一键复制优化输出,提升工作效率。
    113次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议隐私政策
返回登录
  • 重置密码