Merge pull request #187 from IncSW/zero-values-on-custom-types

This commit is contained in:
Masaaki Goshima 2021-04-15 03:04:02 +09:00 committed by GitHub
commit cd300c9e56
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 357 additions and 5 deletions

View File

@ -2,11 +2,20 @@ package json_test
import ( import (
"bytes" "bytes"
stdjson "encoding/json"
"testing" "testing"
"github.com/goccy/go-json" "github.com/goccy/go-json"
) )
type customBool bool
type customBoolWithMarshaler bool
func (b customBoolWithMarshaler) MarshalJSON() ([]byte, error) {
return stdjson.Marshal(bool(b))
}
func TestCoverBool(t *testing.T) { func TestCoverBool(t *testing.T) {
type structBool struct { type structBool struct {
A bool `json:"a"` A bool `json:"a"`
@ -28,6 +37,24 @@ func TestCoverBool(t *testing.T) {
A *bool `json:"a,string"` A *bool `json:"a,string"`
} }
type structCustomBoolOmitEmpty struct {
A customBool `json:"a,omitempty"`
}
type structSecondFieldCustomBoolOmitEmpty struct {
C bool `json:"c"`
A customBool `json:"a,omitempty"`
}
type structCustomBoolWithMarshalerOmitEmpty struct {
A customBoolWithMarshaler `json:"a,omitempty"`
}
type structSecondFieldCustomBoolWithMarshalerOmitEmpty struct {
C bool `json:"c"`
A customBoolWithMarshaler `json:"a,omitempty"`
}
tests := []struct { tests := []struct {
name string name string
data interface{} data interface{}
@ -1400,6 +1427,86 @@ func TestCoverBool(t *testing.T) {
B: false, B: false,
}, },
}, },
{
name: "AnonymousHeadCustomBoolOmitEmpty",
data: struct {
structCustomBoolOmitEmpty
B bool `json:"b,omitempty"`
}{
structCustomBoolOmitEmpty: structCustomBoolOmitEmpty{A: true},
B: false,
},
},
{
name: "AnonymousHeadCustomBoolOmitEmptyFalse",
data: struct {
structCustomBoolOmitEmpty
B bool `json:"b,omitempty"`
}{
structCustomBoolOmitEmpty: structCustomBoolOmitEmpty{},
B: false,
},
},
{
name: "AnonymousHeadSecondFieldCustomBoolOmitEmpty",
data: struct {
structSecondFieldCustomBoolOmitEmpty
B bool `json:"b,omitempty"`
}{
structSecondFieldCustomBoolOmitEmpty: structSecondFieldCustomBoolOmitEmpty{A: true},
B: false,
},
},
{
name: "AnonymousHeadSecondFieldCustomBoolOmitEmptyFalse",
data: struct {
structSecondFieldCustomBoolOmitEmpty
B bool `json:"b,omitempty"`
}{
structSecondFieldCustomBoolOmitEmpty: structSecondFieldCustomBoolOmitEmpty{},
B: false,
},
},
{
name: "AnonymousHeadCustomBoolWithMarshalerOmitEmpty",
data: struct {
structCustomBoolWithMarshalerOmitEmpty
B bool `json:"b,omitempty"`
}{
structCustomBoolWithMarshalerOmitEmpty: structCustomBoolWithMarshalerOmitEmpty{A: true},
B: false,
},
},
{
name: "AnonymousHeadCustomBoolWithMarshalerOmitEmptyFalse",
data: struct {
structCustomBoolWithMarshalerOmitEmpty
B bool `json:"b,omitempty"`
}{
structCustomBoolWithMarshalerOmitEmpty: structCustomBoolWithMarshalerOmitEmpty{},
B: false,
},
},
{
name: "AnonymousHeadSecondFieldCustomBoolWithMarshalerOmitEmpty",
data: struct {
structSecondFieldCustomBoolWithMarshalerOmitEmpty
B bool `json:"b,omitempty"`
}{
structSecondFieldCustomBoolWithMarshalerOmitEmpty: structSecondFieldCustomBoolWithMarshalerOmitEmpty{A: true},
B: false,
},
},
{
name: "AnonymousHeadSecondFieldCustomBoolWithMarshalerOmitEmptyFalse",
data: struct {
structSecondFieldCustomBoolWithMarshalerOmitEmpty
B bool `json:"b,omitempty"`
}{
structSecondFieldCustomBoolWithMarshalerOmitEmpty: structSecondFieldCustomBoolWithMarshalerOmitEmpty{},
B: false,
},
},
{ {
name: "AnonymousHeadBoolString", name: "AnonymousHeadBoolString",
data: struct { data: struct {
@ -1432,6 +1539,86 @@ func TestCoverBool(t *testing.T) {
B: false, B: false,
}, },
}, },
{
name: "PtrAnonymousHeadCustomBoolOmitEmpty",
data: struct {
*structCustomBoolOmitEmpty
B bool `json:"b,omitempty"`
}{
structCustomBoolOmitEmpty: &structCustomBoolOmitEmpty{A: true},
B: false,
},
},
{
name: "PtrAnonymousHeadCustomBoolOmitEmptyFalse",
data: struct {
*structCustomBoolOmitEmpty
B bool `json:"b,omitempty"`
}{
structCustomBoolOmitEmpty: &structCustomBoolOmitEmpty{},
B: false,
},
},
{
name: "PtrAnonymousHeadSecondFieldCustomBoolOmitEmpty",
data: struct {
*structSecondFieldCustomBoolOmitEmpty
B bool `json:"b,omitempty"`
}{
structSecondFieldCustomBoolOmitEmpty: &structSecondFieldCustomBoolOmitEmpty{A: true},
B: false,
},
},
{
name: "PtrAnonymousHeadSecondFieldCustomBoolOmitEmptyFalse",
data: struct {
*structSecondFieldCustomBoolOmitEmpty
B bool `json:"b,omitempty"`
}{
structSecondFieldCustomBoolOmitEmpty: &structSecondFieldCustomBoolOmitEmpty{},
B: false,
},
},
{
name: "PtrAnonymousHeadCustomBoolWithMarshalerOmitEmpty",
data: struct {
*structCustomBoolWithMarshalerOmitEmpty
B bool `json:"b,omitempty"`
}{
structCustomBoolWithMarshalerOmitEmpty: &structCustomBoolWithMarshalerOmitEmpty{A: true},
B: false,
},
},
{
name: "PtrAnonymousHeadCustomBoolWithMarshalerOmitEmptyFalse",
data: struct {
*structCustomBoolWithMarshalerOmitEmpty
B bool `json:"b,omitempty"`
}{
structCustomBoolWithMarshalerOmitEmpty: &structCustomBoolWithMarshalerOmitEmpty{},
B: false,
},
},
{
name: "PtrAnonymousHeadSecondFieldCustomBoolWithMarshalerOmitEmpty",
data: struct {
*structSecondFieldCustomBoolWithMarshalerOmitEmpty
B bool `json:"b,omitempty"`
}{
structSecondFieldCustomBoolWithMarshalerOmitEmpty: &structSecondFieldCustomBoolWithMarshalerOmitEmpty{A: true},
B: false,
},
},
{
name: "PtrAnonymousHeadSecondFieldCustomBoolWithMarshalerOmitEmptyFalse",
data: struct {
*structSecondFieldCustomBoolWithMarshalerOmitEmpty
B bool `json:"b,omitempty"`
}{
structSecondFieldCustomBoolWithMarshalerOmitEmpty: &structSecondFieldCustomBoolWithMarshalerOmitEmpty{},
B: false,
},
},
{ {
name: "PtrAnonymousHeadBoolString", name: "PtrAnonymousHeadBoolString",
data: struct { data: struct {
@ -1464,6 +1651,26 @@ func TestCoverBool(t *testing.T) {
B: true, B: true,
}, },
}, },
{
name: "NilPtrAnonymousHeadCustomBoolOmitEmpty",
data: struct {
*structCustomBoolOmitEmpty
B bool `json:"b,omitempty"`
}{
structCustomBoolOmitEmpty: nil,
B: true,
},
},
{
name: "NilPtrAnonymousHeadCustomBoolWithMarshalerOmitEmpty",
data: struct {
*structCustomBoolWithMarshalerOmitEmpty
B bool `json:"b,omitempty"`
}{
structCustomBoolWithMarshalerOmitEmpty: nil,
B: true,
},
},
{ {
name: "NilPtrAnonymousHeadBoolString", name: "NilPtrAnonymousHeadBoolString",
data: struct { data: struct {
@ -1620,6 +1827,54 @@ func TestCoverBool(t *testing.T) {
structBoolOmitEmpty: structBoolOmitEmpty{A: true}, structBoolOmitEmpty: structBoolOmitEmpty{A: true},
}, },
}, },
{
name: "AnonymousHeadCustomBoolOnlyOmitEmpty",
data: struct {
structCustomBoolOmitEmpty
}{
structCustomBoolOmitEmpty: structCustomBoolOmitEmpty{A: true},
},
},
{
name: "AnonymousHeadSecondFieldCustomBoolOnlyOmitEmpty",
data: struct {
structSecondFieldCustomBoolOmitEmpty
}{
structSecondFieldCustomBoolOmitEmpty: structSecondFieldCustomBoolOmitEmpty{A: true},
},
},
{
name: "AnonymousHeadCustomBoolWithMarshalerOnlyOmitEmpty",
data: struct {
structCustomBoolWithMarshalerOmitEmpty
}{
structCustomBoolWithMarshalerOmitEmpty: structCustomBoolWithMarshalerOmitEmpty{A: true},
},
},
{
name: "AnonymousHeadCustomBoolWithMarshalerOnlyOmitEmptyFalse",
data: struct {
structCustomBoolWithMarshalerOmitEmpty
}{
structCustomBoolWithMarshalerOmitEmpty: structCustomBoolWithMarshalerOmitEmpty{},
},
},
{
name: "AnonymousHeadSecondFieldCustomBoolWithMarshalerOnlyOmitEmpty",
data: struct {
structSecondFieldCustomBoolWithMarshalerOmitEmpty
}{
structSecondFieldCustomBoolWithMarshalerOmitEmpty: structSecondFieldCustomBoolWithMarshalerOmitEmpty{A: true},
},
},
{
name: "AnonymousHeadSecondFieldCustomBoolWithMarshalerOnlyOmitEmptyFalse",
data: struct {
structSecondFieldCustomBoolWithMarshalerOmitEmpty
}{
structSecondFieldCustomBoolWithMarshalerOmitEmpty: structSecondFieldCustomBoolWithMarshalerOmitEmpty{},
},
},
{ {
name: "AnonymousHeadBoolOnlyString", name: "AnonymousHeadBoolOnlyString",
data: struct { data: struct {
@ -1646,6 +1901,54 @@ func TestCoverBool(t *testing.T) {
structBoolOmitEmpty: &structBoolOmitEmpty{A: true}, structBoolOmitEmpty: &structBoolOmitEmpty{A: true},
}, },
}, },
{
name: "PtrAnonymousHeadCustomBoolOnlyOmitEmpty",
data: struct {
*structCustomBoolOmitEmpty
}{
structCustomBoolOmitEmpty: &structCustomBoolOmitEmpty{A: true},
},
},
{
name: "PtrAnonymousHeadSecondFieldCustomBoolOnlyOmitEmpty",
data: struct {
*structSecondFieldCustomBoolOmitEmpty
}{
structSecondFieldCustomBoolOmitEmpty: &structSecondFieldCustomBoolOmitEmpty{A: true},
},
},
{
name: "PtrAnonymousHeadCustomBoolWithMarshalerOnlyOmitEmpty",
data: struct {
*structCustomBoolWithMarshalerOmitEmpty
}{
structCustomBoolWithMarshalerOmitEmpty: &structCustomBoolWithMarshalerOmitEmpty{A: true},
},
},
{
name: "PtrAnonymousHeadCustomBoolWithMarshalerOnlyOmitEmptyFalse",
data: struct {
*structCustomBoolWithMarshalerOmitEmpty
}{
structCustomBoolWithMarshalerOmitEmpty: &structCustomBoolWithMarshalerOmitEmpty{},
},
},
{
name: "PtrAnonymousHeadSecondFieldCustomBoolWithMarshalerOnlyOmitEmpty",
data: struct {
*structSecondFieldCustomBoolWithMarshalerOmitEmpty
}{
structSecondFieldCustomBoolWithMarshalerOmitEmpty: &structSecondFieldCustomBoolWithMarshalerOmitEmpty{A: true},
},
},
{
name: "PtrAnonymousHeadSecondFieldCustomBoolWithMarshalerOnlyOmitEmptyFalse",
data: struct {
*structSecondFieldCustomBoolWithMarshalerOmitEmpty
}{
structSecondFieldCustomBoolWithMarshalerOmitEmpty: &structSecondFieldCustomBoolWithMarshalerOmitEmpty{},
},
},
{ {
name: "PtrAnonymousHeadBoolOnlyString", name: "PtrAnonymousHeadBoolOnlyString",
data: struct { data: struct {
@ -1672,6 +1975,22 @@ func TestCoverBool(t *testing.T) {
structBoolOmitEmpty: nil, structBoolOmitEmpty: nil,
}, },
}, },
{
name: "NilPtrAnonymousHeadCustomBoolOnlyOmitEmpty",
data: struct {
*structCustomBoolOmitEmpty
}{
structCustomBoolOmitEmpty: nil,
},
},
{
name: "NilPtrAnonymousHeadCustomBoolWithMarshalerOnlyOmitEmpty",
data: struct {
*structCustomBoolWithMarshalerOmitEmpty
}{
structCustomBoolWithMarshalerOmitEmpty: nil,
},
},
{ {
name: "NilPtrAnonymousHeadBoolOnlyString", name: "NilPtrAnonymousHeadBoolOnlyString",
data: struct { data: struct {

View File

@ -560,6 +560,14 @@ func AppendIndent(ctx *RuntimeContext, b []byte, indent int) []byte {
func IsNilForMarshaler(v interface{}) bool { func IsNilForMarshaler(v interface{}) bool {
rv := reflect.ValueOf(v) rv := reflect.ValueOf(v)
switch rv.Kind() { switch rv.Kind() {
case reflect.Bool:
return !rv.Bool()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return rv.Int() == 0
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return rv.Uint() == 0
case reflect.Float32, reflect.Float64:
return math.Float64bits(rv.Float()) == 0
case reflect.Interface, reflect.Map, reflect.Ptr, reflect.Func: case reflect.Interface, reflect.Map, reflect.Ptr, reflect.Func:
return rv.IsNil() return rv.IsNil()
case reflect.Slice: case reflect.Slice:

View File

@ -3646,8 +3646,13 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
code = code.NextField code = code.NextField
break break
} }
iface := ptrToInterface(code, p)
if code.Nilcheck && encoder.IsNilForMarshaler(iface) {
code = code.NextField
break
}
b = append(b, code.Key...) b = append(b, code.Key...)
bb, err := appendMarshalJSON(code, b, ptrToInterface(code, p), false) bb, err := appendMarshalJSON(code, b, iface, false)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -3659,8 +3659,13 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
code = code.NextField code = code.NextField
break break
} }
iface := ptrToInterface(code, p)
if code.Nilcheck && encoder.IsNilForMarshaler(iface) {
code = code.NextField
break
}
b = append(b, code.Key...) b = append(b, code.Key...)
bb, err := appendMarshalJSON(code, b, ptrToInterface(code, p), false) bb, err := appendMarshalJSON(code, b, iface, false)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -3646,8 +3646,13 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
code = code.NextField code = code.NextField
break break
} }
iface := ptrToInterface(code, p)
if code.Nilcheck && encoder.IsNilForMarshaler(iface) {
code = code.NextField
break
}
b = append(b, code.EscapedKey...) b = append(b, code.EscapedKey...)
bb, err := appendMarshalJSON(code, b, ptrToInterface(code, p), true) bb, err := appendMarshalJSON(code, b, iface, true)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -3901,10 +3901,15 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
code = code.NextField code = code.NextField
break break
} }
iface := ptrToInterface(code, p)
if code.Nilcheck && encoder.IsNilForMarshaler(iface) {
code = code.NextField
break
}
b = appendIndent(ctx, b, code.Indent) b = appendIndent(ctx, b, code.Indent)
b = append(b, code.EscapedKey...) b = append(b, code.EscapedKey...)
b = append(b, ' ') b = append(b, ' ')
bb, err := appendMarshalJSON(ctx, code, b, ptrToInterface(code, p), code.Indent+1, true) bb, err := appendMarshalJSON(ctx, code, b, iface, code.Indent+1, true)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -3901,10 +3901,15 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
code = code.NextField code = code.NextField
break break
} }
iface := ptrToInterface(code, p)
if code.Nilcheck && encoder.IsNilForMarshaler(iface) {
code = code.NextField
break
}
b = appendIndent(ctx, b, code.Indent) b = appendIndent(ctx, b, code.Indent)
b = append(b, code.Key...) b = append(b, code.Key...)
b = append(b, ' ') b = append(b, ' ')
bb, err := appendMarshalJSON(ctx, code, b, ptrToInterface(code, p), code.Indent+1, false) bb, err := appendMarshalJSON(ctx, code, b, iface, code.Indent+1, false)
if err != nil { if err != nil {
return nil, err return nil, err
} }