From d1aa59e34e2b02811f83d1e25c8536a059e7be4c Mon Sep 17 00:00:00 2001 From: Tao Wen <taowen@gmail.com> Date: Wed, 8 Mar 2017 07:38:25 -0800 Subject: [PATCH] #12 implement omitempty --- feature_reflect.go | 30 ++++++++++++++++ feature_reflect_array.go | 5 +++ feature_reflect_native.go | 64 +++++++++++++++++++++++++++++++++ feature_reflect_object.go | 48 +++++++++++++++++++------ jsoniter_reflect_struct_test.go | 13 +++++++ 5 files changed, 150 insertions(+), 10 deletions(-) diff --git a/feature_reflect.go b/feature_reflect.go index b446812..af4fcc5 100644 --- a/feature_reflect.go +++ b/feature_reflect.go @@ -22,6 +22,7 @@ type Decoder interface { } type Encoder interface { + isEmpty(ptr unsafe.Pointer) bool encode(ptr unsafe.Pointer, stream *Stream) encodeInterface(val interface{}, stream *Stream) } @@ -59,6 +60,10 @@ func (encoder *funcEncoder) encodeInterface(val interface{}, stream *Stream) { WriteToStream(val, stream, encoder) } +func (encoder *funcEncoder) isEmpty(ptr unsafe.Pointer) bool { + return false +} + var DECODERS unsafe.Pointer var ENCODERS unsafe.Pointer @@ -187,6 +192,14 @@ func (encoder *optionalEncoder) encodeInterface(val interface{}, stream *Stream) WriteToStream(val, stream, encoder) } +func (encoder *optionalEncoder) isEmpty(ptr unsafe.Pointer) bool { + if *((*unsafe.Pointer)(ptr)) == nil { + return true + } else { + return encoder.valueEncoder.isEmpty(*((*unsafe.Pointer)(ptr))) + } +} + type mapDecoder struct { mapType reflect.Type elemType reflect.Type @@ -240,6 +253,14 @@ func (encoder *mapEncoder) encodeInterface(val interface{}, stream *Stream) { WriteToStream(val, stream, encoder) } +func (encoder *mapEncoder) isEmpty(ptr unsafe.Pointer) bool { + mapInterface := encoder.mapInterface + mapInterface.word = ptr + realInterface := (*interface{})(unsafe.Pointer(&mapInterface)) + realVal := reflect.ValueOf(*realInterface) + return realVal.Len() == 0 +} + type mapInterfaceEncoder struct { mapType reflect.Type elemType reflect.Type @@ -269,6 +290,15 @@ func (encoder *mapInterfaceEncoder) encodeInterface(val interface{}, stream *Str WriteToStream(val, stream, encoder) } +func (encoder *mapInterfaceEncoder) isEmpty(ptr unsafe.Pointer) bool { + mapInterface := encoder.mapInterface + mapInterface.word = ptr + realInterface := (*interface{})(unsafe.Pointer(&mapInterface)) + realVal := reflect.ValueOf(*realInterface) + + return realVal.Len() == 0 +} + // emptyInterface is the header for an interface{} value. type emptyInterface struct { typ *struct{} diff --git a/feature_reflect_array.go b/feature_reflect_array.go index c0370f2..aa092d4 100644 --- a/feature_reflect_array.go +++ b/feature_reflect_array.go @@ -53,6 +53,11 @@ func (encoder *sliceEncoder) encodeInterface(val interface{}, stream *Stream) { WriteToStream(val, stream, encoder) } +func (encoder *sliceEncoder) isEmpty(ptr unsafe.Pointer) bool { + slice := (*sliceHeader)(ptr) + return slice.Len == 0 +} + type sliceDecoder struct { sliceType reflect.Type elemType reflect.Type diff --git a/feature_reflect_native.go b/feature_reflect_native.go index 4e56e0d..6da523f 100644 --- a/feature_reflect_native.go +++ b/feature_reflect_native.go @@ -19,6 +19,10 @@ func (encoder *stringCodec) encodeInterface(val interface{}, stream *Stream) { WriteToStream(val, stream, encoder) } +func (codec *stringCodec) isEmpty(ptr unsafe.Pointer) bool { + return *((*string)(ptr)) == "" +} + type intCodec struct { } @@ -34,6 +38,10 @@ func (encoder *intCodec) encodeInterface(val interface{}, stream *Stream) { WriteToStream(val, stream, encoder) } +func (codec *intCodec) isEmpty(ptr unsafe.Pointer) bool { + return *((*int)(ptr)) == 0 +} + type int8Codec struct { } @@ -49,6 +57,10 @@ func (encoder *int8Codec) encodeInterface(val interface{}, stream *Stream) { WriteToStream(val, stream, encoder) } +func (codec *int8Codec) isEmpty(ptr unsafe.Pointer) bool { + return *((*int8)(ptr)) == 0 +} + type int16Codec struct { } @@ -64,6 +76,10 @@ func (encoder *int16Codec) encodeInterface(val interface{}, stream *Stream) { WriteToStream(val, stream, encoder) } +func (codec *int16Codec) isEmpty(ptr unsafe.Pointer) bool { + return *((*int16)(ptr)) == 0 +} + type int32Codec struct { } @@ -79,6 +95,10 @@ func (encoder *int32Codec) encodeInterface(val interface{}, stream *Stream) { WriteToStream(val, stream, encoder) } +func (codec *int32Codec) isEmpty(ptr unsafe.Pointer) bool { + return *((*int32)(ptr)) == 0 +} + type int64Codec struct { } @@ -94,6 +114,10 @@ func (encoder *int64Codec) encodeInterface(val interface{}, stream *Stream) { WriteToStream(val, stream, encoder) } +func (codec *int64Codec) isEmpty(ptr unsafe.Pointer) bool { + return *((*int64)(ptr)) == 0 +} + type uintCodec struct { } @@ -109,6 +133,10 @@ func (encoder *uintCodec) encodeInterface(val interface{}, stream *Stream) { WriteToStream(val, stream, encoder) } +func (codec *uintCodec) isEmpty(ptr unsafe.Pointer) bool { + return *((*uint)(ptr)) == 0 +} + type uint8Codec struct { } @@ -124,6 +152,10 @@ func (encoder *uint8Codec) encodeInterface(val interface{}, stream *Stream) { WriteToStream(val, stream, encoder) } +func (codec *uint8Codec) isEmpty(ptr unsafe.Pointer) bool { + return *((*uint8)(ptr)) == 0 +} + type uint16Codec struct { } @@ -139,6 +171,10 @@ func (encoder *uint16Codec) encodeInterface(val interface{}, stream *Stream) { WriteToStream(val, stream, encoder) } +func (codec *uint16Codec) isEmpty(ptr unsafe.Pointer) bool { + return *((*uint16)(ptr)) == 0 +} + type uint32Codec struct { } @@ -154,6 +190,10 @@ func (encoder *uint32Codec) encodeInterface(val interface{}, stream *Stream) { WriteToStream(val, stream, encoder) } +func (codec *uint32Codec) isEmpty(ptr unsafe.Pointer) bool { + return *((*uint32)(ptr)) == 0 +} + type uint64Codec struct { } @@ -169,6 +209,10 @@ func (encoder *uint64Codec) encodeInterface(val interface{}, stream *Stream) { WriteToStream(val, stream, encoder) } +func (codec *uint64Codec) isEmpty(ptr unsafe.Pointer) bool { + return *((*uint64)(ptr)) == 0 +} + type float32Codec struct { } @@ -184,6 +228,10 @@ func (encoder *float32Codec) encodeInterface(val interface{}, stream *Stream) { WriteToStream(val, stream, encoder) } +func (codec *float32Codec) isEmpty(ptr unsafe.Pointer) bool { + return *((*float32)(ptr)) == 0 +} + type float64Codec struct { } @@ -199,6 +247,10 @@ func (encoder *float64Codec) encodeInterface(val interface{}, stream *Stream) { WriteToStream(val, stream, encoder) } +func (codec *float64Codec) isEmpty(ptr unsafe.Pointer) bool { + return *((*float64)(ptr)) == 0 +} + type boolCodec struct { } @@ -214,6 +266,10 @@ func (encoder *boolCodec) encodeInterface(val interface{}, stream *Stream) { WriteToStream(val, stream, encoder) } +func (codec *boolCodec) isEmpty(ptr unsafe.Pointer) bool { + return !(*((*bool)(ptr))) +} + type interfaceCodec struct { } @@ -229,6 +285,10 @@ func (encoder *interfaceCodec) encodeInterface(val interface{}, stream *Stream) stream.WriteVal(val) } +func (codec *interfaceCodec) isEmpty(ptr unsafe.Pointer) bool { + return ptr == nil +} + type anyCodec struct { } @@ -244,6 +304,10 @@ func (encoder *anyCodec) encodeInterface(val interface{}, stream *Stream) { (val.(Any)).WriteTo(stream) } +func (encoder *anyCodec) isEmpty(ptr unsafe.Pointer) bool { + return (*((*Any)(ptr))).Size() == 0 +} + type stringNumberDecoder struct { elemDecoder Decoder } diff --git a/feature_reflect_object.go b/feature_reflect_object.go index be66575..b0fe97b 100644 --- a/feature_reflect_object.go +++ b/feature_reflect_object.go @@ -33,9 +33,16 @@ func encoderOfStruct(typ reflect.Type) (Encoder, error) { fieldNames = []string{tagParts[0]} } } + omitempty := false + for _, tagPart := range tagParts { + if tagPart == "omitempty" { + omitempty = true + } + } var encoder Encoder + var err error if len(fieldNames) > 0 { - encoder, err := encoderOfType(field.Type) + encoder, err = encoderOfType(field.Type) if err != nil { return prefix(fmt.Sprintf("{%s}", field.Name)).addToEncoder(encoder, err) } @@ -46,14 +53,11 @@ func encoderOfStruct(typ reflect.Type) (Encoder, error) { } } for _, fieldName := range fieldNames { - if structEncoder_.firstField == nil { - structEncoder_.firstField = &structFieldEncoder{&field, fieldName, encoder} - } else { - structEncoder_.fields = append(structEncoder_.fields, &structFieldEncoder{&field, fieldName, encoder}) - } + structEncoder_.fields = append(structEncoder_.fields, + &structFieldEncoder{&field, fieldName, encoder, omitempty}) } } - if structEncoder_.firstField == nil { + if len(structEncoder_.fields) == 0 { return &emptyStructEncoder{}, nil } return structEncoder_, nil @@ -1024,6 +1028,7 @@ type structFieldEncoder struct { field *reflect.StructField fieldName string fieldEncoder Encoder + omitempty bool } func (encoder *structFieldEncoder) encode(ptr unsafe.Pointer, stream *Stream) { @@ -1039,18 +1044,28 @@ func (encoder *structFieldEncoder) encodeInterface(val interface{}, stream *Stre WriteToStream(val, stream, encoder) } +func (encoder *structFieldEncoder) isEmpty(ptr unsafe.Pointer) bool { + fieldPtr := uintptr(ptr) + encoder.field.Offset + return encoder.fieldEncoder.isEmpty(unsafe.Pointer(fieldPtr)) +} + type structEncoder struct { - firstField *structFieldEncoder fields []*structFieldEncoder } func (encoder *structEncoder) encode(ptr unsafe.Pointer, stream *Stream) { stream.WriteObjectStart() - encoder.firstField.encode(ptr, stream) + isNotFirst := false for _, field := range encoder.fields { - stream.WriteMore() + if isNotFirst { + stream.WriteMore() + } + if field.omitempty && field.isEmpty(ptr) { + continue + } field.encode(ptr, stream) + isNotFirst = true } stream.WriteObjectEnd() } @@ -1059,6 +1074,15 @@ func (encoder *structEncoder) encodeInterface(val interface{}, stream *Stream) { WriteToStream(val, stream, encoder) } +func (encoder *structEncoder) isEmpty(ptr unsafe.Pointer) bool { + for _, field := range encoder.fields { + if !field.isEmpty(ptr) { + return false + } + } + return true +} + type emptyStructEncoder struct { } @@ -1069,4 +1093,8 @@ func (encoder *emptyStructEncoder) encode(ptr unsafe.Pointer, stream *Stream) { func (encoder *emptyStructEncoder) encodeInterface(val interface{}, stream *Stream) { WriteToStream(val, stream, encoder) +} + +func (encoder *emptyStructEncoder) isEmpty(ptr unsafe.Pointer) bool { + return true } \ No newline at end of file diff --git a/jsoniter_reflect_struct_test.go b/jsoniter_reflect_struct_test.go index d55edf8..78459f3 100644 --- a/jsoniter_reflect_struct_test.go +++ b/jsoniter_reflect_struct_test.go @@ -158,4 +158,17 @@ func Test_mixed(t *testing.T) { should.Nil(err) should.Equal(1, aa.ID) should.Equal("123", aa.Payload["account"]) +} + +func Test_omit_empty(t *testing.T) { + should := require.New(t) + type TestObject struct { + Field1 string `json:"field-1,omitempty"` + Field2 string `json:"field-2,omitempty"` + } + obj := TestObject{} + obj.Field2 = "hello" + str, err := MarshalToString(&obj) + should.Nil(err) + should.Equal(`{"field-2":"hello"}`, str) } \ No newline at end of file