0

0

Go 中高效流式解析 JSON 数组(非换行分隔格式)

聖光之護

聖光之護

发布时间:2026-02-12 12:45:54

|

532人浏览过

|

来源于php中文网

原创

Go 中高效流式解析 JSON 数组(非换行分隔格式)

本文介绍如何在 go 中高性能地流式解析嵌套结构的 json 数组(如 `[{"a":1},{"b":2}]`),避免内存全量加载与重复扫描,推荐使用轻量级 json 扫描器(如 `megajson/scanner`)实现逐对象解码。

在 Go 标准库中,json.Decoder 天然支持流式解析——但仅限于多个独立 JSON 值连续排列(即 newline-delimited JSON,NDJSON),例如:

{"Name":"Ed","Text":"Knock knock."}
{"Name":"Sam","Text":"Who's there?"}

而实际业务中,我们常遇到的是单个 JSON 数组([...])作为整体输入流,例如:

[
  {"Name": "Ed", "Text": "Knock knock."},
  {"Name": "Sam", "Text": "Who's there?"},
  {"Name": "Ed", "Text": "Go fmt."}
]

此时若调用 json.NewDecoder(r).Decode(&v),它会尝试一次性解码整个数组到切片,失去“流式”优势;若强行拆解字符串(如剥去 [] 后按 {...} 切分),又面临括号嵌套、引号转义、性能退化等难题。

推荐方案:基于词法扫描器(Scanner)的增量解析
不依赖完整语法树,而是逐字符识别 JSON 令牌(token),在状态机驱动下实时构建对象。megajson/scanner 是一个轻量、无反射、零分配(核心路径)的纯扫描实现,特别适合高吞吐、低延迟场景。

以下是一个完整可运行示例,解析上述 JSON 数组并构造 []Object:

CodeWP
CodeWP

针对 WordPress 训练的AI代码生成器

下载
package main

import (
    "fmt"
    "strings"

    "github.com/benbjohnson/megajson/scanner"
)

type Object struct {
    Name string `json:"Name"`
    Text string `json:"Text"`
}

func main() {
    rdr := strings.NewReader(`[
        {"Name": "Ed", "Text": "Knock knock."},
        {"Name": "Sam", "Text": "Who's there?"},
        {"Name": "Ed", "Text": "Go fmt."}
    ]`)

    s := scanner.NewScanner(rdr)
    var objects []Object

    // 状态机:跟踪是否处于键名/值上下文、当前对象字段
    state := struct {
        inObject bool   // 是否已进入 { ... }
        inKey    bool   // 当前 STRING token 是否为 key
        lastKey  string
        obj      Object
    }{}

    for {
        tok, data, err := s.Scan()
        if err != nil {
            break // EOF or syntax error
        }

        switch tok {
        case scanner.TLBRACE:
            // '{' → 开始新对象
            state.inObject = true
            state.inKey = true
            state.lastKey = ""
            state.obj = Object{}
        case scanner.TRBRACE:
            // '}' → 对象结束,追加到结果
            if state.inObject {
                objects = append(objects, state.obj)
                state.inObject = false
            }
        case scanner.TSTRING:
            // 字符串 token:交替作为 key 或 value
            str := string(data)
            if state.inObject {
                if state.inKey {
                    state.lastKey = str
                } else {
                    // 根据 lastKey 赋值字段(支持任意嵌套需扩展为 map[string]interface{})
                    switch state.lastKey {
                    case "Name":
                        state.obj.Name = str
                    case "Text":
                        state.obj.Text = str
                    }
                }
                state.inKey = !state.inKey
            }
        // 忽略 TCOMMA、TLBRACKET、TRBRACKET、TNUMBER 等无关 token(本例无嵌套数值/布尔)
        }
    }

    fmt.Printf("Parsed %d objects: %+v\n", len(objects), objects)
}

? 关键设计说明

  • 状态驱动:仅用几个布尔/字符串字段维护解析位置,无递归、无栈分配;
  • 零拷贝字符串提取:data 是 []byte 切片,直接 string(data) 转换(注意:若需长期持有,应显式拷贝);
  • 可扩展性:若需支持嵌套对象或动态字段,可将 state.obj 替换为 map[string]interface{},并在 TLBRACE/TRBRACE 中管理嵌套层级栈;
  • 错误处理:s.Scan() 在非法 JSON 时返回具体错误(如 scanner.ErrInvalidToken),便于日志与熔断。

⚠️ 注意事项

  • megajson/scanner 不验证 JSON 语义完整性(如 key 是否带引号),需确保输入合法;
  • 它不替代 encoding/json 的完整反序列化能力(如 time.Time、自定义 UnmarshalJSON),适用于结构已知、性能敏感的 ETL 或网关场景;
  • 生产环境建议封装为 func ParseJSONStream(r io.Reader, fn func(interface{}) error) error,通过回调处理每个对象,避免累积内存。

总结:当标准 json.Decoder 不适用时,转向底层扫描器是平衡性能与可控性的最优解。它把解析逻辑显式暴露给开发者,既规避了正则/字符串分割的脆弱性,又比 fork 标准库解码器更轻量、更易维护。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
golang如何定义变量
golang如何定义变量

golang定义变量的方法:1、声明变量并赋予初始值“var age int =值”;2、声明变量但不赋初始值“var age int”;3、使用短变量声明“age :=值”等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

206

2024.02.23

golang有哪些数据转换方法
golang有哪些数据转换方法

golang数据转换方法:1、类型转换操作符;2、类型断言;3、字符串和数字之间的转换;4、JSON序列化和反序列化;5、使用标准库进行数据转换;6、使用第三方库进行数据转换;7、自定义数据转换函数。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

235

2024.02.23

golang常用库有哪些
golang常用库有哪些

golang常用库有:1、标准库;2、字符串处理库;3、网络库;4、加密库;5、压缩库;6、xml和json解析库;7、日期和时间库;8、数据库操作库;9、文件操作库;10、图像处理库。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

346

2024.02.23

golang和python的区别是什么
golang和python的区别是什么

golang和python的区别是:1、golang是一种编译型语言,而python是一种解释型语言;2、golang天生支持并发编程,而python对并发与并行的支持相对较弱等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

212

2024.03.05

golang是免费的吗
golang是免费的吗

golang是免费的。golang是google开发的一种静态强类型、编译型、并发型,并具有垃圾回收功能的开源编程语言,采用bsd开源协议。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

402

2024.05.21

golang结构体相关大全
golang结构体相关大全

本专题整合了golang结构体相关大全,想了解更多内容,请阅读专题下面的文章。

322

2025.06.09

golang相关判断方法
golang相关判断方法

本专题整合了golang相关判断方法,想了解更详细的相关内容,请阅读下面的文章。

197

2025.06.10

golang数组使用方法
golang数组使用方法

本专题整合了golang数组用法,想了解更多的相关内容,请阅读专题下面的文章。

784

2025.06.17

2026春节习俗大全
2026春节习俗大全

本专题整合了2026春节习俗大全,阅读专题下面的文章了解更多详细内容。

189

2026.02.11

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
WEB前端教程【HTML5+CSS3+JS】
WEB前端教程【HTML5+CSS3+JS】

共101课时 | 9.1万人学习

JS进阶与BootStrap学习
JS进阶与BootStrap学习

共39课时 | 3.3万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号