Skip to content

Commit 7564cc5

Browse files
authored
Use newer encoding.TextAppender interface in Go 1.24 (#157)
Also, remove special case for netip.Addr, which now implements the encoding.TextAppender interface.
1 parent 1ae217a commit 7564cc5

File tree

7 files changed

+38
-39
lines changed

7 files changed

+38
-39
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ jobs:
99
- name: Install Go
1010
uses: actions/setup-go@v5
1111
with:
12-
go-version: 1.23.x
12+
go-version: 1.24.x
1313
- name: Checkout code
1414
uses: actions/checkout@v4
1515
- name: Test
@@ -26,7 +26,7 @@ jobs:
2626
test-all:
2727
strategy:
2828
matrix:
29-
go-version: [1.23.x]
29+
go-version: [1.24.x]
3030
os: [ubuntu-latest, macos-latest, windows-latest]
3131
runs-on: ${{ matrix.os }}
3232
steps:

arshal.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,30 @@ package json
66

77
import (
88
"bytes"
9+
"encoding"
910
"io"
1011
"reflect"
1112
"slices"
1213
"strings"
1314
"sync"
15+
"time"
1416

1517
"github.com/go-json-experiment/json/internal"
1618
"github.com/go-json-experiment/json/internal/jsonflags"
1719
"github.com/go-json-experiment/json/internal/jsonopts"
1820
"github.com/go-json-experiment/json/jsontext"
1921
)
2022

23+
// Reference encoding and time packages to assist pkgsite
24+
// in being able to hotlink references to those packages.
25+
var (
26+
_ encoding.TextMarshaler
27+
_ encoding.TextAppender
28+
_ encoding.TextUnmarshaler
29+
_ time.Time
30+
_ time.Duration
31+
)
32+
2133
// export exposes internal functionality of the "jsontext" package.
2234
var export = jsontext.Internal.Export(&internal.AllowInternalUse)
2335

@@ -77,6 +89,10 @@ func putStructOptions(o *jsonopts.Struct) {
7789
// - If the value type implements [Marshaler],
7890
// then the MarshalJSON method is called to encode the value.
7991
//
92+
// - If the value type implements [encoding.TextAppender],
93+
// then the AppendText method is called to encode the value and
94+
// subsequently encode its result as a JSON string.
95+
//
8096
// - If the value type implements [encoding.TextMarshaler],
8197
// then the MarshalText method is called to encode the value and
8298
// subsequently encode its result as a JSON string.

arshal_default.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1720,7 +1720,7 @@ func makeInterfaceArshaler(t reflect.Type) *arshaler {
17201720
case jsonMarshalerType:
17211721
v2.Set(reflect.ValueOf(struct{ Marshaler }{va.Elem().Interface().(Marshaler)}))
17221722
case textAppenderType:
1723-
v2.Set(reflect.ValueOf(struct{ encodingTextAppender }{va.Elem().Interface().(encodingTextAppender)}))
1723+
v2.Set(reflect.ValueOf(struct{ encoding.TextAppender }{va.Elem().Interface().(encoding.TextAppender)}))
17241724
case textMarshalerType:
17251725
v2.Set(reflect.ValueOf(struct{ encoding.TextMarshaler }{va.Elem().Interface().(encoding.TextMarshaler)}))
17261726
}

arshal_methods.go

Lines changed: 2 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -24,26 +24,15 @@ var (
2424
jsonMarshalerToType = reflect.TypeFor[MarshalerTo]()
2525
jsonUnmarshalerType = reflect.TypeFor[Unmarshaler]()
2626
jsonUnmarshalerFromType = reflect.TypeFor[UnmarshalerFrom]()
27-
textAppenderType = reflect.TypeFor[encodingTextAppender]()
27+
textAppenderType = reflect.TypeFor[encoding.TextAppender]()
2828
textMarshalerType = reflect.TypeFor[encoding.TextMarshaler]()
2929
textUnmarshalerType = reflect.TypeFor[encoding.TextUnmarshaler]()
3030

31-
// TODO(https://go.dev/issue/62384): Use encoding.TextAppender instead of this hack.
32-
// This exists for now to provide performance benefits to netip types.
33-
// There is no semantic difference with this change.
34-
appenderToType = reflect.TypeFor[interface{ AppendTo([]byte) []byte }]()
35-
3631
allMarshalerTypes = []reflect.Type{jsonMarshalerToType, jsonMarshalerType, textAppenderType, textMarshalerType}
3732
allUnmarshalerTypes = []reflect.Type{jsonUnmarshalerFromType, jsonUnmarshalerType, textUnmarshalerType}
3833
allMethodTypes = append(allMarshalerTypes, allUnmarshalerTypes...)
3934
)
4035

41-
// TODO(https://go.dev/issue/62384): Use encoding.TextAppender instead
42-
// and document public support for this method in json.Marshal.
43-
type encodingTextAppender interface {
44-
AppendText(b []byte) ([]byte, error)
45-
}
46-
4736
// Marshaler is implemented by types that can marshal themselves.
4837
// It is recommended that types implement [MarshalerTo] unless the implementation
4938
// is trying to avoid a hard dependency on the "jsontext" package.
@@ -137,21 +126,6 @@ func makeMethodArshaler(fncs *arshaler, t reflect.Type) *arshaler {
137126
}
138127
return nil
139128
}
140-
// TODO(https://go.dev/issue/62384): Rely on encoding.TextAppender instead.
141-
if implementsAny(t, appenderToType) && t.PkgPath() == "net/netip" {
142-
fncs.marshal = func(enc *jsontext.Encoder, va addressableValue, mo *jsonopts.Struct) error {
143-
appender := va.Addr().Interface().(interface{ AppendTo([]byte) []byte })
144-
if err := export.Encoder(enc).AppendRaw('"', false, func(b []byte) ([]byte, error) {
145-
return appender.AppendTo(b), nil
146-
}); err != nil {
147-
if !isSemanticError(err) && !export.IsIOError(err) {
148-
err = newMarshalErrorBefore(enc, t, err)
149-
}
150-
return err
151-
}
152-
return nil
153-
}
154-
}
155129
}
156130

157131
if needAddr, ok := implements(t, textAppenderType); ok {
@@ -162,7 +136,7 @@ func makeMethodArshaler(fncs *arshaler, t reflect.Type) *arshaler {
162136
(needAddr && va.forcedAddr) {
163137
return prevMarshal(enc, va, mo)
164138
}
165-
appender := va.Addr().Interface().(encodingTextAppender)
139+
appender := va.Addr().Interface().(encoding.TextAppender)
166140
if err := export.Encoder(enc).AppendRaw('"', false, appender.AppendText); err != nil {
167141
err = wrapSkipFunc(err, "append method")
168142
if mo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {

fields_test.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -273,9 +273,17 @@ func TestMakeStructFields(t *testing.T) {
273273
}, {
274274
name: jsontest.Name("InlineTextAppender"),
275275
in: struct {
276-
A struct{ encodingTextAppender } `json:",inline"`
276+
A struct{ encoding.TextAppender } `json:",inline"`
277277
}{},
278-
wantErr: errors.New(`inlined Go struct field A of type struct { json.encodingTextAppender } must not implement marshal or unmarshal methods`),
278+
want: structFields{flattened: []structField{{
279+
index: []int{0, 0},
280+
typ: reflect.TypeFor[encoding.TextAppender](),
281+
fieldOptions: fieldOptions{
282+
name: "TextAppender",
283+
quotedName: `"TextAppender"`,
284+
},
285+
}}},
286+
wantErr: errors.New(`inlined Go struct field A of type struct { encoding.TextAppender } must not implement marshal or unmarshal methods`),
279287
}, {
280288
name: jsontest.Name("UnknownJSONMarshaler"),
281289
in: struct {

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
// This package will regularly experience breaking changes.
44
module github.com/go-json-experiment/json
55

6-
go 1.23
6+
go 1.24

v1/options.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -179,12 +179,13 @@ import (
179179
"github.com/go-json-experiment/json/jsontext"
180180
)
181181

182-
// Reference the jsonv2 and jsontext packages to assist pkgsite
183-
// in being able to hotlink references to those packages.
182+
// Reference encoding, jsonv2, and jsontext packages to assist pkgsite
183+
// in being able to hotlink references to those packages.
184184
var (
185-
_ = jsonv2.Deterministic
186-
_ = jsontext.AllowDuplicateNames
187-
_ = encoding.TextMarshaler(nil)
185+
_ encoding.TextMarshaler
186+
_ encoding.TextUnmarshaler
187+
_ jsonv2.Options
188+
_ jsontext.Options
188189
)
189190

190191
// Options are a set of options to configure the v2 "json" package

0 commit comments

Comments
 (0)