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 (
|
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 {
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue