Skip to content

Commit

Permalink
NilAsEmpty, nil slice=>[], golang/go#37711
Browse files Browse the repository at this point in the history
  • Loading branch information
bingoohuang committed May 4, 2022
1 parent 43c9021 commit 0b0090d
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 13 deletions.
3 changes: 3 additions & 0 deletions pkg/jsoni/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ type Config struct {
ObjectFieldMustBeSimpleString bool
CaseSensitive bool
Int64AsString bool
NilAsEmpty bool
}

// API the public interface of this package.
Expand Down Expand Up @@ -95,6 +96,7 @@ type frozenConfig struct {
iteratorPool *sync.Pool
caseSensitive bool
int64AsString bool
nilAsEmpty bool

typeDecoders map[string]ValDecoder
fieldDecoders map[string]ValDecoder
Expand Down Expand Up @@ -155,6 +157,7 @@ func (c Config) Froze() API {
disallowUnknownFields: c.DisallowUnknownFields,
caseSensitive: c.CaseSensitive,
int64AsString: c.Int64AsString,
nilAsEmpty: c.NilAsEmpty,

typeDecoders: map[string]ValDecoder{},
fieldDecoders: map[string]ValDecoder{},
Expand Down
9 changes: 9 additions & 0 deletions pkg/jsoni/jsoni_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,15 @@ func TestContext(t *testing.T) {
assert.Equal(t, expect, m)
}

func TestMarshalJSONArray(t *testing.T) {
f := struct {
Foo []json.Number `json:",nilasempty"`
}{}

s, _ := jsoni.MarshalToString(f)
assert.Equal(t, `{"Foo":[]}`, s)
}

func TestMarshalJSON(t *testing.T) {
f := struct {
Foo json.Number
Expand Down
17 changes: 12 additions & 5 deletions pkg/jsoni/reflect_extension.go
Original file line number Diff line number Diff line change
Expand Up @@ -370,8 +370,10 @@ func describeStruct(ctx *ctx, typ reflect2.Type) *StructDescriptor {
structDescriptor := describeStruct(ctx, field.Type())
for _, b := range structDescriptor.Fields {
b.levels = append([]int{i}, b.levels...)
omitempty := b.Encoder.(*structFieldEncoder).omitempty
b.Encoder = &structFieldEncoder{field: field, fieldEncoder: b.Encoder, omitempty: omitempty}
fieldEncoder := b.Encoder.(*structFieldEncoder)
omitempty := fieldEncoder.omitempty
nilAsEmpty := fieldEncoder.nilAsEmpty
b.Encoder = &structFieldEncoder{field: field, fieldEncoder: b.Encoder, omitempty: omitempty, nilAsEmpty: nilAsEmpty}
b.Decoder = &structFieldDecoder{field: field, fieldDecoder: b.Decoder}
embeddedBindings = append(embeddedBindings, b)
}
Expand All @@ -382,9 +384,11 @@ func describeStruct(ctx *ctx, typ reflect2.Type) *StructDescriptor {
structDescriptor := describeStruct(ctx, ptrType.Elem())
for _, b := range structDescriptor.Fields {
b.levels = append([]int{i}, b.levels...)
omitempty := b.Encoder.(*structFieldEncoder).omitempty
fieldEncoder := b.Encoder.(*structFieldEncoder)
omitempty := fieldEncoder.omitempty
nilAsEmpty := fieldEncoder.nilAsEmpty
b.Encoder = &dereferenceEncoder{ValueEncoder: b.Encoder}
b.Encoder = &structFieldEncoder{field: field, fieldEncoder: b.Encoder, omitempty: omitempty}
b.Encoder = &structFieldEncoder{field: field, fieldEncoder: b.Encoder, omitempty: omitempty, nilAsEmpty: nilAsEmpty}
b.Decoder = &dereferenceDecoder{valueType: ptrType.Elem(), valueDecoder: b.Decoder}
b.Decoder = &structFieldDecoder{field: field, fieldDecoder: b.Decoder}
embeddedBindings = append(embeddedBindings, b)
Expand Down Expand Up @@ -445,9 +449,12 @@ func (b sortableBindings) Less(i, j int) bool {
func processTags(structDescriptor *StructDescriptor, cfg *frozenConfig) {
for _, b := range structDescriptor.Fields {
shouldOmitEmpty := cfg.omitEmptyStructField
nilAsEmpty := cfg.nilAsEmpty
tagParts := strings.Split(b.Field.Tag().Get(cfg.getTagKey()), ",")
for _, tagPart := range tagParts[1:] {
switch tagPart {
case "nilasempty":
nilAsEmpty = true
case "omitempty":
shouldOmitEmpty = true
case "string":
Expand All @@ -466,7 +473,7 @@ func processTags(structDescriptor *StructDescriptor, cfg *frozenConfig) {
}
}
b.Decoder = &structFieldDecoder{field: b.Field, fieldDecoder: b.Decoder}
b.Encoder = &structFieldEncoder{field: b.Field, fieldEncoder: b.Encoder, omitempty: shouldOmitEmpty}
b.Encoder = &structFieldEncoder{field: b.Field, fieldEncoder: b.Encoder, omitempty: shouldOmitEmpty, nilAsEmpty: nilAsEmpty}
}
}

Expand Down
11 changes: 8 additions & 3 deletions pkg/jsoni/reflect_slice.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,28 @@ import (
func decoderOfSlice(ctx *ctx, typ reflect2.Type) ValDecoder {
sliceType := typ.(*reflect2.UnsafeSliceType)
decoder := decoderOfType(ctx.append("[sliceElem]"), sliceType.Elem())
return &sliceDecoder{sliceType, decoder}
return &sliceDecoder{sliceType: sliceType, elemDecoder: decoder}
}

func encoderOfSlice(ctx *ctx, typ reflect2.Type) ValEncoder {
sliceType := typ.(*reflect2.UnsafeSliceType)
encoder := encoderOfType(ctx.append("[sliceElem]"), sliceType.Elem())
return &sliceEncoder{sliceType, encoder}
return &sliceEncoder{ctx: ctx, sliceType: sliceType, elemEncoder: encoder}
}

type sliceEncoder struct {
ctx *ctx
sliceType *reflect2.UnsafeSliceType
elemEncoder ValEncoder
}

func (e *sliceEncoder) Encode(ctx context.Context, ptr unsafe.Pointer, stream *Stream) {
if e.sliceType.UnsafeIsNil(ptr) {
stream.WriteNil()
if e.ctx.nilAsEmpty {
stream.WriteEmptyArray()
} else {
stream.WriteNil()
}
return
}
length := e.sliceType.UnsafeLengthOf(ptr)
Expand Down
17 changes: 12 additions & 5 deletions pkg/jsoni/reflect_struct_encoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,13 @@ func createCheckIsEmpty(ctx *ctx, typ reflect2.Type) checkIsEmpty {

switch kind := typ.Kind(); kind {
case reflect.Interface:
return &dynamicEncoder{typ}
return &dynamicEncoder{valType: typ}
case reflect.Struct:
return &structEncoder{typ: typ}
case reflect.Array:
return &arrayEncoder{}
case reflect.Slice:
return &sliceEncoder{}
return &sliceEncoder{ctx: ctx}
case reflect.Map:
return encoderOfMap(ctx, typ)
case reflect.Ptr:
Expand Down Expand Up @@ -100,6 +100,7 @@ type structFieldEncoder struct {
field reflect2.StructField
fieldEncoder ValEncoder
omitempty bool
nilAsEmpty bool
}

func (e *structFieldEncoder) Encode(ctx context.Context, ptr unsafe.Pointer, stream *Stream) {
Expand Down Expand Up @@ -142,17 +143,23 @@ func (e *structEncoder) Encode(ctx context.Context, ptr unsafe.Pointer, stream *
stream.WriteObjectStart()
isNotFirst := false
for _, field := range e.fields {
if field.encoder.omitempty && field.encoder.IsEmpty(ctx, ptr, true) {
fieldEncoder := field.encoder
if fieldEncoder.omitempty && fieldEncoder.IsEmpty(ctx, ptr, true) {
continue
}
if field.encoder.IsEmbeddedPtrNil(ptr) {
if fieldEncoder.IsEmbeddedPtrNil(ptr) {
continue
}
if isNotFirst {
stream.WriteMore()
}
stream.WriteObjectField(field.toName)
field.encoder.Encode(ctx, ptr, stream)
if fieldEncoder.nilAsEmpty && fieldEncoder.IsEmpty(ctx, ptr, true) && fieldEncoder.field.Type().Kind() == reflect.Slice {
stream.WriteEmptyArray()
} else {
fieldEncoder.Encode(ctx, ptr, stream)
}

isNotFirst = true
}
stream.WriteObjectEnd()
Expand Down

0 comments on commit 0b0090d

Please sign in to comment.