From 96d9a462d165cdb2ee4be4c10d1790a0459f2fa9 Mon Sep 17 00:00:00 2001 From: Vu Dinh Date: Wed, 3 Jan 2024 10:36:14 -0500 Subject: [PATCH] Address feedbacks Signed-off-by: Vu Dinh --- encode.go | 60 ++++++++++++++++++++++++++++---------------------- encode_test.go | 30 ++++++++++++++++++++----- 2 files changed, 58 insertions(+), 32 deletions(-) diff --git a/encode.go b/encode.go index 3d193acf..82f10bc5 100644 --- a/encode.go +++ b/encode.go @@ -288,13 +288,19 @@ func (m NilContainersMode) valid() bool { type OmitEmptyMode int const ( - // OmitEmptyV1 specifies that the field should be omitted from the encoding - // if the field has an empty value, defined as false, 0, a nil pointer, a nil - // interface value, and any empty array, slice, map, or string. - // This behavior is the same with the current "v1" encoding/json library in go. - OmitEmptyV1 OmitEmptyMode = 1 - - maxOmitEmptyMode = 2 + // OmitEmptyCBORValue specifies that fields tagged with "omitempty" should be + // omitted from encoding if the field would be encoded as an empty CBOR value, + // such as CBOR false, 0, 0.0, nil, empty byte, empty string, empty array, + // empty struct or empty map. + OmitEmptyCBORValue OmitEmptyMode = iota + + // OmitEmptyGoValue specifies that fields tagged with "omitempty" should be + // omitted from encoding if the field has an empty Go value, defined as false, 0, a nil pointer, + // a nil interface value, and any empty array, slice, map, or string. + // This behavior is the same as the current (aka v1) encoding/json package included in Go. + OmitEmptyGoValue + + maxOmitEmptyMode ) func (om OmitEmptyMode) valid() bool { @@ -526,6 +532,7 @@ func (opts EncOptions) encMode() (*encMode, error) { indefLength: opts.IndefLength, nilContainers: opts.NilContainers, tagsMd: opts.TagsMd, + omitEmpty: opts.OmitEmpty, } return &em, nil } @@ -626,7 +633,7 @@ func putEncoderBuffer(e *encoderBuffer) { } type encodeFunc func(e *encoderBuffer, em *encMode, v reflect.Value) error -type isEmptyFunc func(v reflect.Value) (empty bool, err error) +type isEmptyFunc func(em *encMode, v reflect.Value) (empty bool, err error) var ( cborFalse = []byte{0xf4} @@ -1125,16 +1132,13 @@ func encodeStruct(e *encoderBuffer, em *encMode, v reflect.Value) (err error) { } } if f.omitEmpty { - empty, err := f.ief(fv) + empty, err := f.ief(em, fv) if err != nil { putEncoderBuffer(kve) return err } if empty { - // If the emptyMode is set to 1 and the field is a struct, preserve the field name - if !(em.omitEmpty == OmitEmptyV1 && f.typ.Kind() != reflect.Struct) { - continue - } + continue } } @@ -1429,52 +1433,56 @@ func getEncodeIndirectValueFunc(t reflect.Type) encodeFunc { } } -func alwaysNotEmpty(_ reflect.Value) (empty bool, err error) { +func alwaysNotEmpty(em *encMode, _ reflect.Value) (empty bool, err error) { return false, nil } -func isEmptyBool(v reflect.Value) (bool, error) { +func isEmptyBool(em *encMode, v reflect.Value) (bool, error) { return !v.Bool(), nil } -func isEmptyInt(v reflect.Value) (bool, error) { +func isEmptyInt(em *encMode, v reflect.Value) (bool, error) { return v.Int() == 0, nil } -func isEmptyUint(v reflect.Value) (bool, error) { +func isEmptyUint(em *encMode, v reflect.Value) (bool, error) { return v.Uint() == 0, nil } -func isEmptyFloat(v reflect.Value) (bool, error) { +func isEmptyFloat(em *encMode, v reflect.Value) (bool, error) { return v.Float() == 0.0, nil } -func isEmptyString(v reflect.Value) (bool, error) { +func isEmptyString(em *encMode, v reflect.Value) (bool, error) { return v.Len() == 0, nil } -func isEmptySlice(v reflect.Value) (bool, error) { +func isEmptySlice(em *encMode, v reflect.Value) (bool, error) { return v.Len() == 0, nil } -func isEmptyMap(v reflect.Value) (bool, error) { +func isEmptyMap(em *encMode, v reflect.Value) (bool, error) { return v.Len() == 0, nil } -func isEmptyPtr(v reflect.Value) (bool, error) { +func isEmptyPtr(em *encMode, v reflect.Value) (bool, error) { return v.IsNil(), nil } -func isEmptyIntf(v reflect.Value) (bool, error) { +func isEmptyIntf(em *encMode, v reflect.Value) (bool, error) { return v.IsNil(), nil } -func isEmptyStruct(v reflect.Value) (bool, error) { +func isEmptyStruct(em *encMode, v reflect.Value) (bool, error) { structType, err := getEncodingStructType(v.Type()) if err != nil { return false, err } + if em.omitEmpty == OmitEmptyGoValue { + return false, nil + } + if structType.toArray { return len(structType.fields) == 0, nil } @@ -1501,7 +1509,7 @@ func isEmptyStruct(v reflect.Value) (bool, error) { } } - empty, err := f.ief(fv) + empty, err := f.ief(em, fv) if err != nil { return false, err } @@ -1512,7 +1520,7 @@ func isEmptyStruct(v reflect.Value) (bool, error) { return true, nil } -func isEmptyBinaryMarshaler(v reflect.Value) (bool, error) { +func isEmptyBinaryMarshaler(em *encMode, v reflect.Value) (bool, error) { m, ok := v.Interface().(encoding.BinaryMarshaler) if !ok { pv := reflect.New(v.Type()) diff --git a/encode_test.go b/encode_test.go index d1eb9843..044335ca 100644 --- a/encode_test.go +++ b/encode_test.go @@ -1131,8 +1131,25 @@ func TestOmitEmptyMode(t *testing.T) { } v := T{} - // {"b": false, "ui": 0, "i":0, "f": 0, "s": "", "slc": null, "m": {}, "p": nil, "intf": nil, "str": {}, "stro": {}} - want := []byte{ + + // {"b": false, "ui": 0, "i":0, "f": 0, "s": "", "slc": nil, "m": nil, "p": nil, "intf": nil, "str": {}, "stro": {}} + want_govalue := []byte{ + 0xab, + 0x61, 0x62, 0xf4, + 0x62, 0x75, 0x69, 0x00, + 0x61, 0x69, 0x00, + 0x61, 0x66, 0xfb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x61, 0x73, 0x60, + 0x63, 0x73, 0x6c, 0x63, 0xf6, + 0x61, 0x6d, 0xf6, + 0x61, 0x70, 0xf6, + 0x64, 0x69, 0x6e, 0x74, 0x66, 0xf6, + 0x63, 0x73, 0x74, 0x72, 0xa0, + 0x64, 0x73, 0x74, 0x72, 0x6F, 0xa0, + } + + // {"b": false, "ui": 0, "i":0, "f": 0, "s": "", "slc": nil, "m": nil, "p": nil, "intf": nil, "str": nil, "stro": nil} + want_cborvalue := []byte{ 0xaa, 0x61, 0x62, 0xf4, 0x62, 0x75, 0x69, 0x00, @@ -1143,13 +1160,14 @@ func TestOmitEmptyMode(t *testing.T) { 0x61, 0x6d, 0xf6, 0x61, 0x70, 0xf6, 0x64, 0x69, 0x6e, 0x74, 0x66, 0xf6, - 0x63, 0x73, 0x74, 0x72, - 0xa0, + 0x63, 0x73, 0x74, 0x72, 0xa0, } - em, _ := EncOptions{OmitEmpty: OmitEmptyV1}.EncMode() + em_govalue, _ := EncOptions{OmitEmpty: OmitEmptyGoValue}.EncMode() + em_cborvalue, _ := EncOptions{OmitEmpty: OmitEmptyCBORValue}.EncMode() dm, _ := DecOptions{}.DecMode() - testRoundTrip(t, []roundTripTest{{"default values", v, want}}, em, dm) + testRoundTrip(t, []roundTripTest{{"OmitEmptyGoValue default values", v, want_govalue}}, em_govalue, dm) + testRoundTrip(t, []roundTripTest{{"OmitEmptyCBORValue values", v, want_cborvalue}}, em_cborvalue, dm) } func TestOmitEmptyForNestedStruct(t *testing.T) {