当前位置:首页 > 文章列表 > Golang > Go问答 > Go json.Unmarshal 为什么不拒绝缺少必填字段

Go json.Unmarshal 为什么不拒绝缺少必填字段

来源:17golang原创 2026-10-26 19:51:19 0浏览 收藏

Go json.Unmarshal 负责把 JSON 数据解码到 Go 值,不负责判断业务字段是否必填。JSON 对象少了某个结构体字段,通常不会因此报错;新建目标中的字段仍是零值。json 标签用于字段映射,omitempty 影响编码时是否省略字段,都不是解码时的必填规则。

可靠的处理方式是把输入对象与业务对象分开:先解码,再检查字段是否提供、是否允许 null、数值范围及字符串内容,最后才创建可使用的业务值。下面以创建导出任务为例,name 必须有非空内容,limit 必须明确提供且在 1 到 500 之间,enabled 必须明确提供,但 false 是合法值。

Go 官方地址:https://go.dev/

目标与边界:先定义什么叫“必填”

“不能为空”至少有两种含义:键必须出现,或者值必须有业务意义。对 enabled 来说,false 是明确选择,不能用 if !enabled 判断缺失;对 name 来说,提供了空字符串或全空格仍不合格。对 limit 来说,即便键出现,零也不在本例允许范围内。

输入情况本例策略原因
name 缺失或 null拒绝没有可使用名称
name 是空白拒绝业务内容无效
enabled 为 false接受合法布尔选择
limit 为 0拒绝超出约定范围

完整处理链:解码、校验、转换各有职责

输入结构体 CreateInput 使用指针字段,让新建对象中的 nil 表达“未提供可用值”。解码 null 也会得到 nil,因此这套方案把缺失与 null 一起拒绝,能够区分它们和合法的 false。通过校验以后,转换成不含这些输入指针的 ExportJob,让后续代码不必重复判断 nil。

输入字段与指针状态及业务对象的关系结构图
图1:输入契约结构图。CreateInput 的三个指针字段承载输入状态,ExportJob 是校验后使用的业务对象;图为结构说明,不是执行流程或运行结果。

阶段拆解:每次解码使用新的输入对象

package main

import (
    "encoding/json"
    "fmt"
    "strings"
)

type CreateInput struct {
    Name    *string `json:"name"`
    Limit   *int    `json:"limit"`
    Enabled *bool   `json:"enabled"`
}

type ExportJob struct {
    Name    string
    Limit   int
    Enabled bool
}

func parseCreate(data []byte) (ExportJob, error) {
    var in CreateInput // 每次请求新建,避免旧字段值掩盖本次缺失。
    if err := json.Unmarshal(data, &in); err != nil {
        return ExportJob{}, fmt.Errorf("JSON 解码失败: %w", err)
    }
    if in.Name == nil {
        return ExportJob{}, fmt.Errorf("name 必须提供且不能为 null")
    }
    name := strings.TrimSpace(*in.Name)
    if name == "" {
        return ExportJob{}, fmt.Errorf("name 不能是空白")
    }
    if in.Limit == nil {
        return ExportJob{}, fmt.Errorf("limit 必须提供且不能为 null")
    }
    if *in.Limit  500 {
        return ExportJob{}, fmt.Errorf("limit 必须在 1 到 500 之间")
    }
    if in.Enabled == nil {
        return ExportJob{}, fmt.Errorf("enabled 必须提供且不能为 null")
    }
    // 解码和校验成功后,才构造业务对象。
    return ExportJob{Name: name, Limit: *in.Limit, Enabled: *in.Enabled}, nil
}

func main() {
    samples := []string{
        `{}`,
        `{"name":"周报","limit":30,"enabled":false}`,
        `{"name":"周报","limit":30,"enabled":null}`,
        `{"name":"  ","limit":30,"enabled":true}`,
    }
    for _, raw := range samples {
        job, err := parseCreate([]byte(raw))
        fmt.Printf("input=%s job=%+v err=%v\n", raw, job, err)
    }
}

这是完整原创示例,可保存为 main.go 自行运行。按上述规则,只有第二个样本成功,enabled 保留 false;其余分别因缺失名称、空的 enabled 或空白名称失败。这里描述的是代码预期,没有伪造本地测试日志。

校验顺序还决定一次返回哪个错误。示例按字段顺序返回第一项,便于说明;表单需要一次展示全部问题时,可以收集字段级错误,但仍应在解码失败后停止转换,避免把部分解码结果当成完整输入。

推荐流程:明确区分缺失与 null 时再增加状态

指针并不区分全部输入状态。在新建 CreateInput 中,缺失和显式 null 都是 nil;如果更新接口要求“缺失表示不修改,null 表示清空”,就需要单独记录键是否存在。可以先解码成 map[string]json.RawMessage,用键查询的 ok 判断存在,用原始值判断 null,再解码成字段类型。

func readNameState(data []byte) (present, isNull bool, value string, err error) {
    var fields map[string]json.RawMessage
    if err = json.Unmarshal(data, &fields); err != nil {
        return false, false, "", err
    }
    if fields == nil {
        return false, false, "", fmt.Errorf("请求必须是 JSON 对象")
    }
    raw, ok := fields["name"] // 键查询表达是否出现,不能仅看零值。
    if !ok {
        return false, false, "", nil
    }
    if strings.TrimSpace(string(raw)) == "null" {
        return true, true, "", nil
    }
    // 不是 null 时,再要求它是字符串。
    if err = json.Unmarshal(raw, &value); err != nil {
        return true, false, "", fmt.Errorf("name 类型错误: %w", err)
    }
    return true, false, value, nil
}

该函数片段使用上例已经导入的 json、fmt、strings。它只演示 name 的三态,不是完整更新协议。基于 map 的精确键查询要求使用 name 这个大小写;与结构体解码可能接受大小写不敏感匹配的行为不同,接口应统一约定,避免两层规则互相矛盾。

RawMessage 键存在状态与 null 和字段值的结构关系
图2:字段存在状态结构图。fields 的键查询区分缺失与出现,原始值可进一步区分 null 和具体字段值;这是一张静态关系图。

常见误区:严格解码并不自动补上业务规则

DisallowUnknownFields 用于拒绝对象里无法匹配目标结构体字段的键,解决的是“多了陌生字段”,不是“少了必要字段”。同理,给结构体写 validate:"required" 标签,不会让标准库自行执行第三方验证逻辑;采用某个验证库时,要明确调用它并确认零值语义。

不要复用已装有旧数据的输入对象处理下一次完整创建请求。缺失字段可能留下此前的值,使“必填”检查出现假通过。新建对象是本例的一部分;对更新接口有意保留旧值时,也应通过清楚的合并逻辑完成,而不是隐式依赖复用对象。

若改用 Decoder,单次 Decode 可以只读一个 JSON 值。只允许一个对象的接口还应拒绝尾部第二个值,并限制请求体大小。不要为了额外选项从 Unmarshal 换成 Decoder 后,忘记输入边界发生变化。重复键、大小写规则和数字类型也需要按具体协议另行约束,本文的必填校验不等于完整 JSON Schema 实现。

速查:根据字段语义选表示方式

需求表示方式仍需处理
缺失与零值都无效普通值加业务校验也可范围、内容规则
false 或 0 合法,但必须提供新建输入对象的指针字段null 是否允许
缺失、null、具体值各有含义存在标记或 RawMessage键名与类型约定
后续逻辑只想使用合格数据校验后转换业务对象错误返回与状态一致性

“必填”应当写成可读的接口契约。解码成功后明确做校验,再让数据进入存储或任务队列,能够避免把传输层成功误认为业务操作成功。

相关问题

omitempty 能要求请求字段出现吗?

不能。它用于编码省略规则,不是解码时的必填约束。

bool 用指针后,false 会不会仍被当成没填?

只检查指针是否 nil 就不会。false 对应非 nil 指针,取值后仍然是 false。

缺失字段会自动覆盖成零值吗?

不要这样理解。本文每次新建输入对象,所以初始零值自然保留;对已有对象解码时,未出现的结构体字段可能继续保留原值。

参考:json.Unmarshal、DisallowUnknownFields、RawMessage。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Go json.Encoder.SetEscapeHTML 为什么会改变字符串输出Go json.Encoder.SetEscapeHTML 为什么会改变字符串输出
上一篇
Go json.Encoder.SetEscapeHTML 为什么会改变字符串输出
Go errors.Is 为什么需要自定义 Is 方法
下一篇
Go errors.Is 为什么需要自定义 Is 方法
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之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推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    718次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    724次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    801次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    760次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    594次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码