Fix encoding custom types zero values with omitempty when marshaller exists

This commit is contained in:
IncSW 2021-04-11 21:45:59 +03:00
parent c74c2078ea
commit b57146602e
No known key found for this signature in database
GPG Key ID: 89876FA64BFB2D57
3 changed files with 198 additions and 1 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,14 @@ func TestCoverBool(t *testing.T) {
A *bool `json:"a,string"` A *bool `json:"a,string"`
} }
type structCustomBoolOmitEmpty struct {
A customBool `json:"a,omitempty"`
}
type structCustomBoolWithMarshalerOmitEmpty struct {
A customBoolWithMarshaler `json:"a,omitempty"`
}
tests := []struct { tests := []struct {
name string name string
data interface{} data interface{}
@ -1400,6 +1417,46 @@ 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: "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: "AnonymousHeadBoolString", name: "AnonymousHeadBoolString",
data: struct { data: struct {
@ -1432,6 +1489,46 @@ 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: "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: "PtrAnonymousHeadBoolString", name: "PtrAnonymousHeadBoolString",
data: struct { data: struct {
@ -1464,6 +1561,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 +1737,30 @@ func TestCoverBool(t *testing.T) {
structBoolOmitEmpty: structBoolOmitEmpty{A: true}, structBoolOmitEmpty: structBoolOmitEmpty{A: true},
}, },
}, },
{
name: "AnonymousHeadCustomBoolOnlyOmitEmpty",
data: struct {
structCustomBoolOmitEmpty
}{
structCustomBoolOmitEmpty: structCustomBoolOmitEmpty{A: true},
},
},
{
name: "AnonymousHeadCustomBoolWithMarshalerOnlyOmitEmpty",
data: struct {
structCustomBoolWithMarshalerOmitEmpty
}{
structCustomBoolWithMarshalerOmitEmpty: structCustomBoolWithMarshalerOmitEmpty{A: true},
},
},
{
name: "AnonymousHeadCustomBoolWithMarshalerOnlyOmitEmptyFalse",
data: struct {
structCustomBoolWithMarshalerOmitEmpty
}{
structCustomBoolWithMarshalerOmitEmpty: structCustomBoolWithMarshalerOmitEmpty{},
},
},
{ {
name: "AnonymousHeadBoolOnlyString", name: "AnonymousHeadBoolOnlyString",
data: struct { data: struct {
@ -1646,6 +1787,30 @@ func TestCoverBool(t *testing.T) {
structBoolOmitEmpty: &structBoolOmitEmpty{A: true}, structBoolOmitEmpty: &structBoolOmitEmpty{A: true},
}, },
}, },
{
name: "PtrAnonymousHeadCustomBoolOnlyOmitEmpty",
data: struct {
*structCustomBoolOmitEmpty
}{
structCustomBoolOmitEmpty: &structCustomBoolOmitEmpty{A: true},
},
},
{
name: "PtrAnonymousHeadCustomBoolWithMarshalerOnlyOmitEmpty",
data: struct {
*structCustomBoolWithMarshalerOmitEmpty
}{
structCustomBoolWithMarshalerOmitEmpty: &structCustomBoolWithMarshalerOmitEmpty{A: true},
},
},
{
name: "PtrAnonymousHeadCustomBoolWithMarshalerOnlyOmitEmptyFalse",
data: struct {
*structCustomBoolWithMarshalerOmitEmpty
}{
structCustomBoolWithMarshalerOmitEmpty: &structCustomBoolWithMarshalerOmitEmpty{},
},
},
{ {
name: "PtrAnonymousHeadBoolOnlyString", name: "PtrAnonymousHeadBoolOnlyString",
data: struct { data: struct {
@ -1672,6 +1837,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,17 @@ 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.Complex64, reflect.Complex128:
c := rv.Complex()
return math.Float64bits(real(c)) == 0 && math.Float64bits(imag(c)) == 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.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
} }