diff --git a/encode_test.go b/encode_test.go index e71a9992..20f92845 100644 --- a/encode_test.go +++ b/encode_test.go @@ -21,6 +21,17 @@ import ( "github.com/goccy/go-json" ) +// ptrTextMarshaler implements encoding.TextMarshaler with a pointer receiver +// that dereferences the receiver, so calling MarshalText on a nil +// *ptrTextMarshaler panics. A nil element in []*ptrTextMarshaler must therefore +// be encoded as null rather than crashing (mirrors the []*time.Time fix from +// #524, extended to the text-marshaler and indent encode paths). +type ptrTextMarshaler struct{ s string } + +func (t *ptrTextMarshaler) MarshalText() ([]byte, error) { + return []byte(t.s), nil +} + type recursiveT struct { A *recursiveT `json:"a,omitempty"` B *recursiveU `json:"b,omitempty"` @@ -431,6 +442,21 @@ func Test_Marshal(t *testing.T) { assertErr(t, err) assertEq(t, "[]*time.Time", `[null]`, string(bytes)) }) + t.Run("[]*time.Time indent", func(t *testing.T) { + bytes, err := json.MarshalIndent([]*time.Time{nil}, "", " ") + assertErr(t, err) + assertEq(t, "[]*time.Time indent", "[\n null\n]", string(bytes)) + }) + t.Run("[]*ptrTextMarshaler", func(t *testing.T) { + bytes, err := json.Marshal([]*ptrTextMarshaler{nil}) + assertErr(t, err) + assertEq(t, "[]*ptrTextMarshaler", `[null]`, string(bytes)) + }) + t.Run("[]*ptrTextMarshaler indent", func(t *testing.T) { + bytes, err := json.MarshalIndent([]*ptrTextMarshaler{nil}, "", " ") + assertErr(t, err) + assertEq(t, "[]*ptrTextMarshaler indent", "[\n null\n]", string(bytes)) + }) }) t.Run("array", func(t *testing.T) { diff --git a/internal/encoder/encoder.go b/internal/encoder/encoder.go index b436f5b2..cb02e01a 100644 --- a/internal/encoder/encoder.go +++ b/internal/encoder/encoder.go @@ -459,6 +459,11 @@ func AppendMarshalJSONIndent(ctx *RuntimeContext, code *Opcode, b []byte, v inte rv = newV } } + + if rv.Kind() == reflect.Ptr && rv.IsNil() { + return AppendNull(ctx, b), nil + } + v = rv.Interface() var bb []byte if (code.Flags & MarshalerContextFlags) != 0 { @@ -509,6 +514,11 @@ func AppendMarshalText(ctx *RuntimeContext, code *Opcode, b []byte, v interface{ rv = newV } } + + if rv.Kind() == reflect.Ptr && rv.IsNil() { + return AppendNull(ctx, b), nil + } + v = rv.Interface() marshaler, ok := v.(encoding.TextMarshaler) if !ok { @@ -532,6 +542,11 @@ func AppendMarshalTextIndent(ctx *RuntimeContext, code *Opcode, b []byte, v inte rv = newV } } + + if rv.Kind() == reflect.Ptr && rv.IsNil() { + return AppendNull(ctx, b), nil + } + v = rv.Interface() marshaler, ok := v.(encoding.TextMarshaler) if !ok {