求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
相关产品推荐
相关产品推荐

