From b57146602eb6de8c2cddcefadcc8b985ffec8bd2 Mon Sep 17 00:00:00 2001 From: IncSW Date: Sun, 11 Apr 2021 21:45:59 +0300 Subject: [PATCH 1/6] Fix encoding custom types zero values with omitempty when marshaller exists --- cover_bool_test.go | 181 ++++++++++++++++++++++++++++++ internal/encoder/encoder.go | 11 ++ internal/encoder/vm_escaped/vm.go | 7 +- 3 files changed, 198 insertions(+), 1 deletion(-) diff --git a/cover_bool_test.go b/cover_bool_test.go index 6dd6cc5..49415fa 100644 --- a/cover_bool_test.go +++ b/cover_bool_test.go @@ -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 { diff --git a/internal/encoder/encoder.go b/internal/encoder/encoder.go index a7371ec..e0226d0 100644 --- a/internal/encoder/encoder.go +++ b/internal/encoder/encoder.go @@ -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: diff --git a/internal/encoder/vm_escaped/vm.go b/internal/encoder/vm_escaped/vm.go index b0c608f..846119b 100644 --- a/internal/encoder/vm_escaped/vm.go +++ b/internal/encoder/vm_escaped/vm.go @@ -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 } From ac14c18dbb90d15b8a5ad903b921690636a89581 Mon Sep 17 00:00:00 2001 From: IncSW Date: Mon, 12 Apr 2021 10:23:57 +0300 Subject: [PATCH 2/6] remove zero value checker for complex --- internal/encoder/encoder.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/internal/encoder/encoder.go b/internal/encoder/encoder.go index e0226d0..5363f52 100644 --- a/internal/encoder/encoder.go +++ b/internal/encoder/encoder.go @@ -568,9 +568,6 @@ func IsNilForMarshaler(v interface{}) bool { 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: From d12ffe2c6649040c44ac8837aabce98ec94bbd7e Mon Sep 17 00:00:00 2001 From: IncSW Date: Mon, 12 Apr 2021 10:26:32 +0300 Subject: [PATCH 3/6] revert vm OpStructFieldOmitEmptyMarshalJSON --- internal/encoder/vm_escaped/vm.go | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/internal/encoder/vm_escaped/vm.go b/internal/encoder/vm_escaped/vm.go index 846119b..b0c608f 100644 --- a/internal/encoder/vm_escaped/vm.go +++ b/internal/encoder/vm_escaped/vm.go @@ -3646,13 +3646,8 @@ 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, iface, true) + bb, err := appendMarshalJSON(code, b, ptrToInterface(code, p), true) if err != nil { return nil, err } From 42adc0a5abf951d4b6a3915141b413a14ee6fb9b Mon Sep 17 00:00:00 2001 From: IncSW Date: Mon, 12 Apr 2021 11:41:10 +0300 Subject: [PATCH 4/6] extend tests with custom types --- cover_bool_test.go | 138 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) diff --git a/cover_bool_test.go b/cover_bool_test.go index 49415fa..27954f8 100644 --- a/cover_bool_test.go +++ b/cover_bool_test.go @@ -41,10 +41,20 @@ func TestCoverBool(t *testing.T) { 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 { name string data interface{} @@ -1437,6 +1447,26 @@ func TestCoverBool(t *testing.T) { 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 { @@ -1457,6 +1487,26 @@ func TestCoverBool(t *testing.T) { 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", data: struct { @@ -1509,6 +1559,26 @@ func TestCoverBool(t *testing.T) { 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 { @@ -1529,6 +1599,26 @@ func TestCoverBool(t *testing.T) { 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", data: struct { @@ -1745,6 +1835,14 @@ func TestCoverBool(t *testing.T) { structCustomBoolOmitEmpty: structCustomBoolOmitEmpty{A: true}, }, }, + { + name: "AnonymousHeadSecondFieldCustomBoolOnlyOmitEmpty", + data: struct { + structSecondFieldCustomBoolOmitEmpty + }{ + structSecondFieldCustomBoolOmitEmpty: structSecondFieldCustomBoolOmitEmpty{A: true}, + }, + }, { name: "AnonymousHeadCustomBoolWithMarshalerOnlyOmitEmpty", data: struct { @@ -1761,6 +1859,22 @@ func TestCoverBool(t *testing.T) { structCustomBoolWithMarshalerOmitEmpty: structCustomBoolWithMarshalerOmitEmpty{}, }, }, + { + name: "AnonymousHeadSecondFieldCustomBoolWithMarshalerOnlyOmitEmpty", + data: struct { + structSecondFieldCustomBoolWithMarshalerOmitEmpty + }{ + structSecondFieldCustomBoolWithMarshalerOmitEmpty: structSecondFieldCustomBoolWithMarshalerOmitEmpty{A: true}, + }, + }, + { + name: "AnonymousHeadSecondFieldCustomBoolWithMarshalerOnlyOmitEmptyFalse", + data: struct { + structSecondFieldCustomBoolWithMarshalerOmitEmpty + }{ + structSecondFieldCustomBoolWithMarshalerOmitEmpty: structSecondFieldCustomBoolWithMarshalerOmitEmpty{}, + }, + }, { name: "AnonymousHeadBoolOnlyString", data: struct { @@ -1795,6 +1909,14 @@ func TestCoverBool(t *testing.T) { structCustomBoolOmitEmpty: &structCustomBoolOmitEmpty{A: true}, }, }, + { + name: "PtrAnonymousHeadSecondFieldCustomBoolOnlyOmitEmpty", + data: struct { + *structSecondFieldCustomBoolOmitEmpty + }{ + structSecondFieldCustomBoolOmitEmpty: &structSecondFieldCustomBoolOmitEmpty{A: true}, + }, + }, { name: "PtrAnonymousHeadCustomBoolWithMarshalerOnlyOmitEmpty", data: struct { @@ -1811,6 +1933,22 @@ func TestCoverBool(t *testing.T) { structCustomBoolWithMarshalerOmitEmpty: &structCustomBoolWithMarshalerOmitEmpty{}, }, }, + { + name: "PtrAnonymousHeadSecondFieldCustomBoolWithMarshalerOnlyOmitEmpty", + data: struct { + *structSecondFieldCustomBoolWithMarshalerOmitEmpty + }{ + structSecondFieldCustomBoolWithMarshalerOmitEmpty: &structSecondFieldCustomBoolWithMarshalerOmitEmpty{A: true}, + }, + }, + { + name: "PtrAnonymousHeadSecondFieldCustomBoolWithMarshalerOnlyOmitEmptyFalse", + data: struct { + *structSecondFieldCustomBoolWithMarshalerOmitEmpty + }{ + structSecondFieldCustomBoolWithMarshalerOmitEmpty: &structSecondFieldCustomBoolWithMarshalerOmitEmpty{}, + }, + }, { name: "PtrAnonymousHeadBoolOnlyString", data: struct { From 53590387e6f1a043389f54a49ba5ef71afa2efd1 Mon Sep 17 00:00:00 2001 From: IncSW Date: Mon, 12 Apr 2021 11:41:31 +0300 Subject: [PATCH 5/6] fix OpStructFieldOmitEmptyMarshalJSON with custom type marshaller on all VMs --- internal/encoder/vm/vm.go | 7 ++++++- internal/encoder/vm_escaped/vm.go | 7 ++++++- internal/encoder/vm_escaped_indent/vm.go | 7 ++++++- internal/encoder/vm_indent/vm.go | 7 ++++++- 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/internal/encoder/vm/vm.go b/internal/encoder/vm/vm.go index 6522548..b4b80b6 100644 --- a/internal/encoder/vm/vm.go +++ b/internal/encoder/vm/vm.go @@ -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.Key...) - bb, err := appendMarshalJSON(code, b, ptrToInterface(code, p), false) + bb, err := appendMarshalJSON(code, b, iface, false) if err != nil { return nil, err } diff --git a/internal/encoder/vm_escaped/vm.go b/internal/encoder/vm_escaped/vm.go index b0c608f..846119b 100644 --- a/internal/encoder/vm_escaped/vm.go +++ b/internal/encoder/vm_escaped/vm.go @@ -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 } diff --git a/internal/encoder/vm_escaped_indent/vm.go b/internal/encoder/vm_escaped_indent/vm.go index 00c03d9..bcddbc1 100644 --- a/internal/encoder/vm_escaped_indent/vm.go +++ b/internal/encoder/vm_escaped_indent/vm.go @@ -3901,10 +3901,15 @@ 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 = appendIndent(ctx, b, code.Indent) b = append(b, code.EscapedKey...) 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 { return nil, err } diff --git a/internal/encoder/vm_indent/vm.go b/internal/encoder/vm_indent/vm.go index 518ddaf..eddffcc 100644 --- a/internal/encoder/vm_indent/vm.go +++ b/internal/encoder/vm_indent/vm.go @@ -3901,10 +3901,15 @@ 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 = appendIndent(ctx, b, code.Indent) b = append(b, code.Key...) 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 { return nil, err } From a0bf49d201604f83fcad80ad13d797eb38cbd52f Mon Sep 17 00:00:00 2001 From: IncSW Date: Wed, 14 Apr 2021 20:49:10 +0300 Subject: [PATCH 6/6] update debug vm --- internal/encoder/vm_debug/vm.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/internal/encoder/vm_debug/vm.go b/internal/encoder/vm_debug/vm.go index 585699b..b78d813 100644 --- a/internal/encoder/vm_debug/vm.go +++ b/internal/encoder/vm_debug/vm.go @@ -3659,8 +3659,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.Key...) - bb, err := appendMarshalJSON(code, b, ptrToInterface(code, p), false) + bb, err := appendMarshalJSON(code, b, iface, false) if err != nil { return nil, err }