登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  Golang >  Go问答

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

来源:17golang原创

时间:2026-07-27 11:08:01 458浏览 收藏

所属专题:Go JSON 工程实践专题

日志采集接口收到的经常不是单个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删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>