From 30686c4cc89e6952ec00846e34016f37d53f31dd Mon Sep 17 00:00:00 2001 From: Joe Tsai Date: Mon, 8 Sep 2025 17:33:47 -0700 Subject: [PATCH] encoding/json/v2: document context annotation with SemanticError When the json package calls Marshaler, MarshalerTo, Unmarshaler, or UnmarshalerFrom methods and a SemanticError is returned, it will automatically annotate the error with context. Document this behavior. Change-Id: Id8e775a7c1c2a6ffc29ea518913591915e8aff87 Reviewed-on: https://go-review.googlesource.com/c/go/+/701956 Reviewed-by: Damien Neil Reviewed-by: Dmitri Shuralyov LUCI-TryBot-Result: Go LUCI --- src/encoding/json/v2/arshal_methods.go | 18 ++++++++++++++++++ src/encoding/json/v2/errors.go | 5 +++++ .../json/v2/example_orderedobject_test.go | 4 +++- 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/encoding/json/v2/arshal_methods.go b/src/encoding/json/v2/arshal_methods.go index 664c3927cc..2decd144db 100644 --- a/src/encoding/json/v2/arshal_methods.go +++ b/src/encoding/json/v2/arshal_methods.go @@ -41,6 +41,10 @@ var ( // // It is recommended that implementations return a buffer that is safe // for the caller to retain and potentially mutate. +// +// If the returned error is a [SemanticError], then unpopulated fields +// of the error may be populated by [json] with additional context. +// Errors of other types are wrapped within a [SemanticError]. type Marshaler interface { MarshalJSON() ([]byte, error) } @@ -54,6 +58,11 @@ type Marshaler interface { // // The implementation must write only one JSON value to the Encoder and // must not retain the pointer to [jsontext.Encoder]. +// +// If the returned error is a [SemanticError], then unpopulated fields +// of the error may be populated by [json] with additional context. +// Errors of other types are wrapped within a [SemanticError], +// unless it is an IO error. type MarshalerTo interface { MarshalJSONTo(*jsontext.Encoder) error @@ -72,6 +81,10 @@ type MarshalerTo interface { // unmarshaling into a pre-populated value. // // Implementations must not retain or mutate the input []byte. +// +// If the returned error is a [SemanticError], then unpopulated fields +// of the error may be populated by [json] with additional context. +// Errors of other types are wrapped within a [SemanticError]. type Unmarshaler interface { UnmarshalJSON([]byte) error } @@ -88,6 +101,11 @@ type Unmarshaler interface { // unmarshaling into a pre-populated value. // // Implementations must not retain the pointer to [jsontext.Decoder]. +// +// If the returned error is a [SemanticError], then unpopulated fields +// of the error may be populated by [json] with additional context. +// Errors of other types are wrapped within a [SemanticError], +// unless it is a [jsontext.SyntacticError] or an IO error. type UnmarshalerFrom interface { UnmarshalJSONFrom(*jsontext.Decoder) error diff --git a/src/encoding/json/v2/errors.go b/src/encoding/json/v2/errors.go index 940b720210..9485d7b527 100644 --- a/src/encoding/json/v2/errors.go +++ b/src/encoding/json/v2/errors.go @@ -62,6 +62,11 @@ func isFatalError(err error, flags jsonflags.Flags) bool { // SemanticError describes an error determining the meaning // of JSON data as Go data or vice-versa. // +// If a [Marshaler], [MarshalerTo], [Unmarshaler], or [UnmarshalerFrom] method +// returns a SemanticError when called by the [json] package, +// then the ByteOffset, JSONPointer, and GoType fields are automatically +// populated by the calling context if they are the zero value. +// // The contents of this error as produced by this package may change over time. type SemanticError struct { requireKeyedLiterals diff --git a/src/encoding/json/v2/example_orderedobject_test.go b/src/encoding/json/v2/example_orderedobject_test.go index d68782f725..fc23132504 100644 --- a/src/encoding/json/v2/example_orderedobject_test.go +++ b/src/encoding/json/v2/example_orderedobject_test.go @@ -53,7 +53,9 @@ func (obj *OrderedObject[V]) MarshalJSONTo(enc *jsontext.Encoder) error { // UnmarshalJSONFrom decodes a JSON object from dec into obj. func (obj *OrderedObject[V]) UnmarshalJSONFrom(dec *jsontext.Decoder) error { if k := dec.PeekKind(); k != '{' { - return fmt.Errorf("expected object start, but encountered %v", k) + // The [json] package automatically populates relevant fields + // in a [json.SemanticError] to provide additional context. + return &json.SemanticError{JSONKind: k} } if _, err := dec.ReadToken(); err != nil { return err -- 2.52.0