You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求Go中区分JSON零值、Null与Undefined的可靠序列化方案

区分Go JSON中的零值、Null与Undefined的可靠方案

Go中JSON相关问题十分常见,标准库encoding/json使用繁琐,社区正讨论其v2版本。在此之前,需要找到可靠方案区分JSON中的可空值、可选/未定义值与零值。

Null、Undefined与零值通常具有不同语义(例如JSON Merge Patch规范),但Go会将它们统一视为Go零值。使用指针可区分Null/Undefined与有效值,但无法同时区分Null、Undefined与有效值。

以下是为区分零值、Null与Undefined所做的三次尝试:

尝试1:序列化Undefined失败,语法直观

type Optional[T any] struct {
    Value *T
}
func (o *Optional[T]) MarshalJSON() ([]byte, error) { return json.Marshal(o.Value)}
func (o *Optional[T]) UnmarshalJSON(data []byte) error { o.Value = new(T); return json.Unmarshal(data, o.Value) }

type X struct {
    Y Optional[*int] `json:"y,omitempty"` // 尝试实现可选且可空
}

该方案特性:

  • ✅ 反序列化{} -> X.Y.Value为nil
  • ✅ 反序列化{"y": null} -> X.Y.Value非nil,*X.Y.Value为nil
  • ✅ 反序列化{"y": 0} -> X.Y.Value非nil,*X.Y.Value非nil,**X.Y.Value为0
  • ❌ 序列化X.Y.Value为nil时,无法序列化为{},而是序列化为{"y":null}
    • 标准库encoding/json的omitempty不作用于零值结构体
  • ✅ 序列化X.Y.Value非nil、*X.Y.Value为nil时 -> {"y": null}
  • ✅ 序列化X.Y.Value非nil、*X.Y.Value非nil、**X.Y.Value为0时 -> {"y": 0}

尝试2:反序列化Null失败,需使用*Optional[T]实现真正可选

type Optional[T any] struct {
    Value T `json:",inline"`
}
func (o *Optional[T]) MarshalJSON() ([]byte, error)    { return json.Marshal(&o.Value) }
func (o *Optional[T]) UnmarshalJSON(data []byte) error { return json.Unmarshal(data, &o.Value) }

type X struct {
    Y *Optional[*int] `json:"y,omitempty"` // 尝试实现可选且可空
}

该方案特性:

  • ✅ 反序列化{} -> X.Y为nil
  • ❌ 反序列化{"y": null} -> X.Y为nil
    • 因X.Y本身是指针(*Optional[*int]),Null值会将X.Y设为nil,而非调用Optional[*int].UnmarshalJSON
  • ✅ 反序列化{"y": 0} -> X.Y非nil,*X.Y.Value非nil,*X.Y.Value为0
  • ✅ 序列化X.Y为nil时 -> {}
  • ✅ 序列化X.Y非nil、X.Y.Value为nil时 -> {"y": null}
  • ✅ 序列化X.Y非nil、X.Y.Value非nil、*X.Y.Value为0时 -> {"y": 0}

尝试3:可正常工作,但不安全,无法通过go vet检查,语法不够简洁

type Optional[T any] uintptr
func (o *Optional[T]) SetValue(t T)                 { *o = Optional[T](unsafe.Pointer(&t)) }
func (o *Optional[T]) UnsetValue()                  { *o = 0 }
func (o *Optional[T]) Value() *T                    { return (*T)(unsafe.Pointer(*o)) }
func (o *Optional[T]) MarshalJSON() ([]byte, error) { return json.Marshal((*T)(unsafe.Pointer(*o))) }
func (o *Optional[T]) UnmarshalJSON(data []byte) error {
    t := new(T)
    err := json.Unmarshal(data, t)
    if err != nil {
        return err
    }
    *o = Optional[T](unsafe.Pointer(t))
    return nil
}

type X struct {
    Y Optional[*int] `json:"y,omitempty"` // 尝试实现可选且可空
}

该方案特性:

  • ✅ 反序列化{} -> X.Y.Value()为nil
  • ✅ 反序列化{"y": null} -> X.Y.Value()非nil,*X.Y.Value()为nil
  • ✅ 反序列化{"y": 0} -> X.Y.Value()非nil,*X.Y.Value()非nil,**X.Y.Value()为0
  • ✅ 序列化X.Y.UnsetValue()时 -> {}
  • ✅ 序列化X.Y.SetValue(nil)时 -> {"y": null}
  • ✅ 序列化X.Y.SetValue(<pointer to 0>)时 -> {"y": 0}

总结

偏好尝试1的语法风格:

  • Undefined:Optional[*Type].Value为nil
  • Null:*Optional[*Type].Value为nil
  • 有效值:**Optional[*Type].Value为对应值

现寻求一种可在序列化与反序列化时,可靠区分JSON零值、Null与Undefined的解决方案,请问是否有合适的实现方式?

内容的提问来源于stack exchange,提问作者crunk1

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 10:27:03