Skip to content

Commit 9cac083

Browse files
json: omitzero support
1 parent f712280 commit 9cac083

File tree

2 files changed

+38
-4
lines changed

2 files changed

+38
-4
lines changed

json/codec.go

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,7 @@ func appendStructFields(fields []structField, t reflect.Type, offset uintptr, se
568568
anonymous = f.Anonymous
569569
tag = false
570570
omitempty = false
571+
omitzero = false
571572
stringify = false
572573
unexported = len(f.PkgPath) != 0
573574
)
@@ -593,6 +594,8 @@ func appendStructFields(fields []structField, t reflect.Type, offset uintptr, se
593594
switch tag {
594595
case "omitempty":
595596
omitempty = true
597+
case "omitzero":
598+
omitzero = true
596599
case "string":
597600
stringify = true
598601
}
@@ -675,9 +678,11 @@ func appendStructFields(fields []structField, t reflect.Type, offset uintptr, se
675678
fields = append(fields, structField{
676679
codec: codec,
677680
offset: offset + f.Offset,
678-
empty: emptyFuncOf(f.Type),
681+
isEmpty: emptyFuncOf(f.Type),
682+
isZero: zeroFuncOf(f.Type),
679683
tag: tag,
680684
omitempty: omitempty,
685+
omitzero: omitzero,
681686
name: name,
682687
index: i << 32,
683688
typ: f.Type,
@@ -895,6 +900,18 @@ func isValidTag(s string) bool {
895900
return true
896901
}
897902

903+
func zeroFuncOf(t reflect.Type) emptyFunc {
904+
if t.Implements(isZeroerType) {
905+
return func(p unsafe.Pointer) bool {
906+
return unsafeToAny(t, p).(isZeroer).IsZero()
907+
}
908+
}
909+
910+
return func(p unsafe.Pointer) bool {
911+
return reflectDeref(t, p).IsZero()
912+
}
913+
}
914+
898915
func emptyFuncOf(t reflect.Type) emptyFunc {
899916
switch t {
900917
case bytesType, rawMessageType:
@@ -908,7 +925,7 @@ func emptyFuncOf(t reflect.Type) emptyFunc {
908925
}
909926

910927
case reflect.Map:
911-
return func(p unsafe.Pointer) bool { return reflect.NewAt(t, p).Elem().Len() == 0 }
928+
return func(p unsafe.Pointer) bool { return reflectDeref(t, p).Len() == 0 }
912929

913930
case reflect.Slice:
914931
return func(p unsafe.Pointer) bool { return (*slice)(p).len == 0 }
@@ -953,6 +970,14 @@ func emptyFuncOf(t reflect.Type) emptyFunc {
953970
return func(unsafe.Pointer) bool { return false }
954971
}
955972

973+
func reflectDeref(t reflect.Type, p unsafe.Pointer) reflect.Value {
974+
return reflect.NewAt(t, p).Elem()
975+
}
976+
977+
func unsafeToAny(t reflect.Type, p unsafe.Pointer) any {
978+
return reflectDeref(t, p).Interface()
979+
}
980+
956981
type iface struct {
957982
typ unsafe.Pointer
958983
ptr unsafe.Pointer
@@ -976,9 +1001,11 @@ type structType struct {
9761001
type structField struct {
9771002
codec codec
9781003
offset uintptr
979-
empty emptyFunc
1004+
isEmpty emptyFunc
1005+
isZero emptyFunc
9801006
tag bool
9811007
omitempty bool
1008+
omitzero bool
9821009
json string
9831010
html string
9841011
name string
@@ -1064,6 +1091,8 @@ type sliceHeader struct {
10641091
Cap int
10651092
}
10661093

1094+
type isZeroer interface{ IsZero() bool }
1095+
10671096
var (
10681097
nullType = reflect.TypeOf(nil)
10691098
boolType = reflect.TypeFor[bool]()
@@ -1111,6 +1140,7 @@ var (
11111140
jsonUnmarshalerType = reflect.TypeFor[Unmarshaler]()
11121141
textMarshalerType = reflect.TypeFor[encoding.TextMarshaler]()
11131142
textUnmarshalerType = reflect.TypeFor[encoding.TextUnmarshaler]()
1143+
isZeroerType = reflect.TypeFor[isZeroer]()
11141144

11151145
bigIntDecoder = constructJSONUnmarshalerDecodeFunc(bigIntType, false)
11161146
)

json/encode.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -748,7 +748,11 @@ func (e encoder) encodeStruct(b []byte, p unsafe.Pointer, st *structType) ([]byt
748748
f := &st.fields[i]
749749
v := unsafe.Pointer(uintptr(p) + f.offset)
750750

751-
if f.omitempty && f.empty(v) {
751+
switch {
752+
case f.omitempty && f.isEmpty(v):
753+
continue
754+
755+
case f.omitzero && f.isZero(v):
752756
continue
753757
}
754758

0 commit comments

Comments
 (0)