当前位置:首页 > 文章列表 > Golang > Go问答 > Go jsonnull 怎么处理JSON 数组

Go jsonnull 怎么处理JSON 数组

来源:17golang原创 2026-09-13 10:12:01 0浏览 收藏

Go 里处理 JSON 数组时,最容易踩坑的是把 null[] 当成同一种“没有数据”。用标准库 encoding/json 解码到切片时,null 会得到 nil 切片,空数组会得到非 nil 但长度为 0 的切片;重新编码时,前者默认输出 null,后者输出 []。如果接口契约要求“数组字段永远是数组”,就要在输出边界主动归一化。

要点速览
  • null[] 都能解码成功,但 nil 判断结果不同。
  • 普通结构体切片无法直接区分“字段缺失”和“字段为 null”,需要 RawMessage 或指针。
  • omitempty 会同时忽略 nil 切片和零长度切片,不能拿它表达三态业务语义。

先把 Go jsonnull 和空数组的差异看清

假设接口返回一个名为 items 的数组。最小实验不需要引入第三方包,直接让两个变量分别接收 null[]

package main

import (
    "encoding/json"
    "fmt"
)

func main() {
    var fromNull []string
    var fromEmpty []string

    // null 表示没有切片值,[] 表示已经存在但没有元素的切片。
    _ = json.Unmarshal([]byte(`null`), &fromNull)
    _ = json.Unmarshal([]byte(`[]`), &fromEmpty)

    // 用 nil 和 len 同时观察两种状态,避免只看元素数量。
    fmt.Println(fromNull == nil, len(fromNull))   // true 0
    fmt.Println(fromEmpty == nil, len(fromEmpty)) // false 0

    // 重新编码时,两种状态会保留到 JSON 表示中。
    nullJSON, _ := json.Marshal(fromNull)
    emptyJSON, _ := json.Marshal(fromEmpty)
    fmt.Println(string(nullJSON))  // null
    fmt.Println(string(emptyJSON)) // []
}

这里的关键不是长度,而是 fromNull == nil。两者都能安全地进行 lenrange 和追加,但它们表达的协议语义不同。标准库文档也明确说明,JSON null 解码到切片时会把 Go 值设为 nil;空数组则替换为一个新的空切片。

Go JSON null 与空数组解码关系的静态技术框图
图1:Go jsonnull 读取关系示意图,区分 JSON 输入、解码器和两种切片状态;这是结构示意图,不是运行截图。

字段缺失、null 和 [] 为什么还不一样

把切片直接放进结构体时,字段缺失和显式 null 最后都可能留下 nil,因此普通写法只能解决“数组还是空数组”的一部分问题:

type Payload struct {
    Items []string `json:"items"`
}

func decodePayload(data []byte) (Payload, error) {
    var payload Payload

    // 解码结果需要返回给调用方,不能只依赖后续 len 判断。
    if err := json.Unmarshal(data, &payload); err != nil {
        return Payload{}, err
    }
    return payload, nil
}

如果业务只关心“有没有可迭代的元素”,这样足够;如果要区分字段没传、传了 null、传了空数组,则可以先保留原始字段:

type RawPayload struct {
    Items json.RawMessage `json:"items"`
}

func inspectItems(data []byte) (string, error) {
    var payload RawPayload

    // RawMessage 让字段存在性先被保留下来,再决定如何转换。
    if err := json.Unmarshal(data, &payload); err != nil {
        return "invalid-json", err
    }
    if payload.Items == nil {
        return "missing", nil
    }
    if string(payload.Items) == "null" {
        return "null", nil
    }

    var items []string
    if err := json.Unmarshal(payload.Items, &items); err != nil {
        return "invalid-items", err
    }
    if items == nil {
        return "null", nil
    }
    return "array", nil
}

生产代码里不建议只用字符串比较来处理复杂 JSON;示例的目的只是突出三态判断。正式转换时可以把 RawMessage 交给第二次 json.Unmarshal,并把类型错误返回给上层,而不是静默把对象或字符串当数组。

回写接口时,先决定要输出 null 还是 []

很多“Go jsonnull”问题其实发生在响应编码阶段:数据库查询没有结果时切片保持 niljson.Marshal 就会返回 null。若前端协议约定数组字段固定为数组,可以在组装响应时归一化:

type Response struct {
    Items []string `json:"items"`
}

func normalizeItems(items []string) Response {
    // 对外协议要求数组时,把 nil 转成非 nil 的空切片。
    if items == nil {
        items = make([]string, 0)
    }
    return Response{Items: items}
}

反过来,如果 null 有“尚未计算”“不适用”之类的业务含义,就不要无条件转成 []。这时应在领域模型中保留一个明确的存在状态,或使用指针/自定义类型,让响应层能表达这个含义。

Go 切片编码为 JSON null 或空数组的静态关系图
图2:Go jsonnull 输出边界示意图,展示 nil 切片、空切片、Marshal 和接口字段之间的静态关系;这是结果示意图,不是运行证据。

omitempty 不是三态字段开关

json:"items,omitempty" 会在切片为 nil 或长度为 0 时省略字段。它适合“空集合不需要传输”的接口,但不适合同时表达“缺失、null、空数组”三种状态。

可以按下面的规则做选择:

输入或内部状态默认解码结果默认编码结果适合场景
nullnil 切片null未提供、未知或不适用
[]非 nil 空切片[]明确表示集合为空
字段缺失通常保持零值按标签决定兼容旧客户端或可选字段

最后检查四件事:解码后是否需要 nil 判断;接口是否允许 null;写响应前是否统一初始化空切片;omitempty 是否会误删字段。只要这四个问题先定下来,Go 对 JSON 数组的处理就不会被“长度都是 0”带偏。

相关问题

Go 的 nil 切片能直接 range 吗?

可以。对 nil 切片使用 rangelenappend 都是安全的,但编码结果仍可能与空切片不同。

为什么接口返回的数组偶尔变成 null?

常见原因是响应模型中的切片从未初始化,仍是 nil;在编码前把它归一化为空切片,或按协议保留 null 的业务含义即可。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
JSON_TABLE 嵌套数组怎么配置或排查JSON_TABLE 嵌套数组怎么配置或排查
上一篇
JSON_TABLE 嵌套数组怎么配置或排查
Lovart新手怎么建立最小Brand Kit?Logo、颜色和字体配置步骤
下一篇
Lovart新手怎么建立最小Brand Kit?Logo、颜色和字体配置步骤
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之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测试功能,助您快速选择最适合项目的高性能大语言模型。
    111次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    31次使用
  • OpenCompass大模型评测体系详解:功能、使用指南与应用场景
    OpenCompass
    OpenCompass是上海AI实验室推出的开源大模型评测平台,提供CompassKit、CompassHub和CompassRank三大核心组件,支持LLM及多模态模型的一站式标准化评估与排行榜查询。
    48次使用
  • AGI-Eval大模型评测平台:权威榜单、数据集与人机协同评测方案
    AGI-Eval
    AGI-Eval是由上海交大等高校联合发布的大模型评测社区,提供公正透明的LLM能力榜单、多领域评测集及Data Studio数据服务,助力AI模型性能评估与NLP科研开发。
    30次使用
  • SuperCLUE中文大模型评测基准:功能、能力维度与应用指南
    SuperCLUE
    SuperCLUE是权威的中文大语言模型综合评测基准,涵盖语言理解、知识应用、AI Agent智能体及安全性等12项核心能力。通过多轮对话与客观测试,定期发布榜单与技术报告,为模型研发、优化及行业选型提供科学依据。
    265次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议隐私政策
返回登录
  • 重置密码