JSON 工具箱

空值用 null 还是省略字段

结论:`null` 表示「这个字段存在,但没有值」,省略表示「这个字段不存在」。PATCH 接口、配置合并、前端表单回填三处最容易因此出错,判断标准是「消费方需不需要知道这个字段被显式清空」。

进阶约 3 分钟发布于 2026-10-30更新于 2026-10-30
null 与字段省略的语义差异

结论:null 表示字段存在但值为空,省略表示字段不存在,选错会直接改变业务含义;PATCH 请求必须用 null 表示清空、省略表示不修改,返回体与配置合并则各自统一约定。

语义差异

{ "nickname": null }

字段存在,值是空。消费方知道「这个字段被显式清空过」。

{ }

字段不存在。消费方无法判断是「没设置」还是「不支持」。

在 JavaScript 里这个差异非常具体:

'nickname' in obj          // false —— 字段不存在
obj.nickname === null      // true  —— 字段存在且为 null
obj.nickname === undefined // true  —— 两种情况都成立

三处必须区分的地方

一、PATCH 请求。

PATCH /users/1
{ "nickname": null }

含义是「把昵称清空」。

PATCH /users/1
{ }

含义是「什么都不改」。如果 PATCH 实现里把「值为 null」和「字段缺失」当成一回事,用户就永远没法清空昵称——只能改成空字符串,而空字符串和 null 在业务上往往是两回事。

正确实现要用「字段是否存在」判断,而不是「值是否为 null」:

if (Object.hasOwn(patch, 'nickname')) {
  target.nickname = patch.nickname;   // 允许写成 null
}

二、配置合并。

# 默认配置
logLevel: info
# 用户配置(省略)
# 结果:info
# 用户配置(显式 null)
logLevel: null
# 结果:关闭日志输出

这层语义在 Helm、Kubernetes 的 values 合并里非常常见:省略表示「继承默认」,null 表示「显式禁用」。

三、前端表单回填。

输入框拿到 null 应该显示为空,拿到 undefined(字段不存在)通常也显示为空,但提交时要区分:原本有值、现在清空 → 提交 null;从未填过 → 不提交这个字段。不区分会导致「清空操作被当成未修改」。

常规建议

场景 建议
查询接口的返回体 保留 null,保持结构稳定,前端类型好写
PATCH 请求体 严格区分:null 清空,省略不改
PUT 全量替换 可以省略,但语义是「整份覆盖」,缺失字段视为删除
配置合并 省略 = 用默认值,null = 显式关闭
数据库存储 null 表示未知/不适用;空字符串表示「已知为空」

返回体尽量保持结构稳定是重要的一条。列表接口里有些对象带 avatar、有些不带,前端每次都要 'avatar' in item 判断,很容易出错。统一返回 avatar: null 更好写。

别做的三件事

别用空字符串代替 null。 "" 和 null 在业务上经常不同:昵称为空串是「用户设置了空昵称」,null 是「没设置」。混用之后无法回溯。

别用 0 或 -1 表示「没有值」。 数值字段里 0 是合法值,当哨兵值用会让统计逻辑出错。

别在同一个字段上混用两种约定。 有的接口返回 null,有的省略字段,前端就得写两套判断。约定一次,写进文档。

常见问题

如何判断一个字段该用 null 还是直接省略?

消费方需要区分「字段被显式清空」和「字段从未修改」时必须用 null。典型场景是 PATCH 请求:传 null 表示清空该字段,不传表示保持原值。

PATCH 接口如何区分「清空」和「不修改」?

用「字段是否存在」判断,而不是「值是否为 null」。例如用 Object.hasOwn(patch, 'nickname') 判断后再赋值,这样 null 才能作为合法值生效。

为什么返回体应该尽量保留 null?

保留 null 能让返回结构稳定,前端不必对每个可选字段写 'avatar' in item 判断。列表接口里有些对象带某字段、有些不带,会显著增加消费方出错的概率。

相关工具

延伸阅读