Go encoding/json 怎样用 Decoder 流式读取多个对象完成一个可复用的最小示例
当输入不是一个很大的 JSON 数组,而是连续写入的多个对象时,最小而稳妥的做法是为同一个 io.Reader 创建一个 json.Decoder,然后反复调用 Decode。每次调用消费一个对象;读到 io.EOF 表示对象流正常结束,中途出现的语法错误则应该原样返回。
官方文档:https://pkg.go.dev/encoding/json
把“读完了”和“读坏了”分开处理:只有 io.EOF 是成功结束,其他错误都要保留给调用方。下面的函数还会把空输入当作空集合,适合复用在文件、HTTP 响应体和管道输入上。
先分清连续对象和 JSON 数组
下面两种输入看起来都包含多个对象,但读取方式不同。数组是一个完整 JSON 值,通常可以一次解码到切片;连续对象则是多个相邻的 JSON 值,中间允许有空白字符,应该让同一个 Decoder 逐个消费。
[{"id":1,"name":"北京"},{"id":2,"name":"杭州"}]
{"id":1,"name":"北京"}
{"id":2,"name":"杭州"}
如果把第二种输入交给 json.Unmarshal,它会把整段文本当成一个 JSON 文档,遇到后面的对象就无法继续。Decoder 的价值在于它维护输入流状态,让下一次 Decode 从上一次结束的位置继续读取。

把 Decode 循环封装成可复用函数
为了让调用方不必重复写 EOF 判断,可以把读取逻辑封装成泛型函数。函数只负责“从流中读对象”,不绑定文件或网络来源;调用方传入任何实现了 io.Reader 的对象即可。
package main
import (
"encoding/json"
"errors"
"fmt"
"io"
)
type Record struct {
// 字段名与 JSON 键对应,Decode 会把每个对象填入一个新值。
ID int `json:"id"`
Name string `json:"name"`
}
// DecodeStream 逐个读取 JSON 对象,适用于连续 JSON 值而不是单个数组。
func DecodeStream[T any](r io.Reader) ([]T, error) {
dec := json.NewDecoder(r) // 一个 Reader 只配一个 Decoder,保持流位置连续。
items := make([]T, 0)
for index := 0; ; index++ {
var item T
err := dec.Decode(&item) // 每次只消费一个 JSON 值。
if errors.Is(err, io.EOF) {
// EOF 表示正常读完;空输入也自然得到空切片。
return items, nil
}
if err != nil {
// 保留对象序号和底层错误,方便定位坏数据所在位置。
return nil, fmt.Errorf("decode object %d: %w", index, err)
}
items = append(items, item)
}
}
这里没有把 io.EOF 当成普通失败:它是循环的结束信号。相反,第二个对象缺少右括号、字段类型不匹配等错误都会带着对象序号返回,调用方可以决定重试、丢弃整批还是记录坏输入。
用小输入确认空白、EOF 和错误边界
最小调用方可以使用 strings.NewReader 构造连续对象。真实项目中把它替换为打开的文件、HTTP 响应体或消息管道即可,读取函数本身不需要改变。
package main
import (
"fmt"
"strings"
)
func main() {
input := strings.NewReader(`
{"id":1,"name":"北京"}
{"id":2,"name":"杭州"}
`)
items, err := DecodeStream[Record](input)
if err != nil {
// 这里把中途的语法错误交给上层,而不是把部分结果当成完整批次。
fmt.Println("read failed:", err)
return
}
// 成功时 items 只包含已经完整解码的对象。
fmt.Printf("read %d records: %+v\n", len(items), items)
}
| 输入情况 | 函数结果 | 处理建议 |
|---|---|---|
| 空输入或末尾空白 | 空切片或已收集对象,error 为 nil | 视为正常结束 |
| 连续对象全部合法 | 按输入顺序返回对象集合 | 交给业务处理 |
| 中途 JSON 不完整 | error 包含对象序号 | 记录原始错误,不伪装成 EOF |
| 顶层是一个数组 | 也能 Decode 一次 | 按数组协议另写切片解码路径 |

接入文件和 HTTP 响应时注意什么
Decoder 读取的是流,不会替调用方关闭文件或响应体。文件和响应体仍要由创建它们的代码负责 Close。另外,官方文档说明 Decoder 可能从底层 Reader 预读超过当前 JSON 值;如果同一个 Reader 后面还要交给别的解析器,不要在 Decoder 和另一个读取器之间随意切换,必要时统一由 Decoder 管理后续内容。
如果数据规模很大,返回 []T 仍然会把所有对象留在内存中。此时可以把函数改成“每次 Decode 后调用一个处理函数”,让业务消费一个对象就释放本轮引用;但 EOF 判断、错误包装和单 Decoder 原则保持不变。
相关问题
连续 JSON 对象必须用换行分隔吗?
不必须,JSON 空白可以是换行、空格或制表符;换行只是日志和批处理文件更容易阅读。对象之间不能直接拼成一个无效的对象文本。
为什么不能用 Decoder.More 判断顶层对象流?
More 主要用于数组或对象内部的下一个元素判断。顶层连续对象应直接循环调用 Decode,以 io.EOF 结束。
读到一半报错时要不要返回已读对象?
示例选择返回 nil 和错误,避免调用方误把部分批次当成完整数据。如果业务允许部分成功,可以改为同时返回 items,但必须在接口说明中明确这一语义。
Go encoding/json 怎样区分缺失字段和显式 null处理空输入并保留错误边界
- 上一篇
- Go encoding/json 怎样区分缺失字段和显式 null处理空输入并保留错误边界
- 下一篇
- Go encoding/json 怎样用 Decoder 流式读取多个对象处理空输入并保留错误边界
-
- Golang · Go教程 | 9分钟前 |
- Go unicode.ToTitle 怎样把 rune 转成标题大小写
- 430浏览 收藏
-
- Golang · Go教程 | 14分钟前 | Go encoding/json 流式读取 json.Decoder
- Go encoding/json 怎样用 Decoder 流式读取多个对象处理空输入并保留错误边界
- 462浏览 收藏
-
- Golang · Go教程 | 19分钟前 |
- Go encoding/json 怎样区分缺失字段和显式 null处理空输入并保留错误边界
- 203浏览 收藏
-
- Golang · Go教程 | 20分钟前 |
- Go encoding/json 怎样区分缺失字段和显式 null在批处理代码中减少额外分配
- 295浏览 收藏
-
- Golang · Go教程 | 24分钟前 | 错误处理 · Go教程 · Go optional encoding/json json.RawMessage 部分更新 JSON null
- Go encoding/json 怎样区分缺失字段和显式 null完成一个可复用的最小示例
- 260浏览 收藏
-
- Golang · Go教程 | 28分钟前 |
- Go unique.Handle 怎样比较唯一句柄而不复制字符串处理空输入并保留错误边界
- 340浏览 收藏
-
- Golang · Go教程 | 28分钟前 |
- Go unique.Handle 怎样比较唯一句柄而不复制字符串在批处理代码中减少额外分配
- 466浏览 收藏
-
- Golang · Go教程 | 30分钟前 | go · 泛型 · Go 字符串比较 unique.Make unique.Handle 规范化值
- Go unique.Handle 怎样比较唯一句柄而不复制字符串完成一个可复用的最小示例
- 364浏览 收藏
-
- Golang · Go教程 | 31分钟前 |
- Go unique.Make 怎样缓存可比较字符串的唯一值在批处理代码中减少额外分配
- 413浏览 收藏
-
- Golang · Go教程 | 32分钟前 |
- Go unique.Make 怎样缓存可比较字符串的唯一值处理空输入并保留错误边界
- 107浏览 收藏
-
- Golang · Go教程 | 36分钟前 |
- Go range over func 怎样让函数迭代器支持提前停止在批处理代码中减少额外分配
- 190浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 920次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 925次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 1024次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 1003次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 831次使用
-
- GScript 编写标准库示例详解
- 2022-12-30 369浏览
-
- 关于Golang标准库flag的全面讲解
- 2023-02-25 344浏览
-
- Go代码规范错误处理示例经验总结
- 2022-12-23 278浏览
-
- Golang标准库unsafe源码解读
- 2022-12-29 464浏览
-
- 快速掌握Go语言HTTP标准库的实现方法
- 2022-12-30 327浏览

