mirror of https://github.com/goccy/go-json.git
Fix encoding custom types zero values with omitempty when marshaller exists
This commit is contained in:
parent
c74c2078ea
commit
b57146602e
|
@ -2,11 +2,20 @@ package json_test
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
stdjson "encoding/json"
|
||||
"testing"
|
||||
|
||||
"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) {
|
||||
type structBool struct {
|
||||
A bool `json:"a"`
|
||||
|
@ -28,6 +37,14 @@ func TestCoverBool(t *testing.T) {
|
|||
A *bool `json:"a,string"`
|
||||
}
|
||||
|
||||
type structCustomBoolOmitEmpty struct {
|
||||
A customBool `json:"a,omitempty"`
|
||||
}
|
||||
|
||||
type structCustomBoolWithMarshalerOmitEmpty struct {
|
||||
A customBoolWithMarshaler `json:"a,omitempty"`
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
data interface{}
|
||||
|
@ -1400,6 +1417,46 @@ func TestCoverBool(t *testing.T) {
|
|||
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",
|
||||
data: struct {
|
||||
|
@ -1432,6 +1489,46 @@ func TestCoverBool(t *testing.T) {
|
|||
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",
|
||||
data: struct {
|
||||
|
@ -1464,6 +1561,26 @@ func TestCoverBool(t *testing.T) {
|
|||
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",
|
||||
data: struct {
|
||||
|
@ -1620,6 +1737,30 @@ func TestCoverBool(t *testing.T) {
|
|||
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",
|
||||
data: struct {
|
||||
|
@ -1646,6 +1787,30 @@ func TestCoverBool(t *testing.T) {
|
|||
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",
|
||||
data: struct {
|
||||
|
@ -1672,6 +1837,22 @@ func TestCoverBool(t *testing.T) {
|
|||
structBoolOmitEmpty: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "NilPtrAnonymousHeadCustomBoolOnlyOmitEmpty",
|
||||
data: struct {
|
||||
*structCustomBoolOmitEmpty
|
||||
}{
|
||||
structCustomBoolOmitEmpty: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "NilPtrAnonymousHeadCustomBoolWithMarshalerOnlyOmitEmpty",
|
||||
data: struct {
|
||||
*structCustomBoolWithMarshalerOmitEmpty
|
||||
}{
|
||||
structCustomBoolWithMarshalerOmitEmpty: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "NilPtrAnonymousHeadBoolOnlyString",
|
||||
data: struct {
|
||||
|
|
|
@ -560,6 +560,17 @@ func AppendIndent(ctx *RuntimeContext, b []byte, indent int) []byte {
|
|||
func IsNilForMarshaler(v interface{}) bool {
|
||||
rv := reflect.ValueOf(v)
|
||||
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:
|
||||
return rv.IsNil()
|
||||
case reflect.Slice:
|
||||
|
|
|
@ -3646,8 +3646,13 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
|
|||
code = code.NextField
|
||||
break
|
||||
}
|
||||
iface := ptrToInterface(code, p)
|
||||
if code.Nilcheck && encoder.IsNilForMarshaler(iface) {
|
||||
code = code.NextField
|
||||
break
|
||||
}
|
||||
b = append(b, code.EscapedKey...)
|
||||
bb, err := appendMarshalJSON(code, b, ptrToInterface(code, p), true)
|
||||
bb, err := appendMarshalJSON(code, b, iface, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue