"fmt"
"reflect"
"strconv"
+ "strings"
"encoding/json/internal/jsonwire"
"encoding/json/jsontext"
func (e *UnmarshalTypeError) Error() string {
var s string
if e.Struct != "" || e.Field != "" {
- s = "json: cannot unmarshal " + e.Value + " into Go struct field " + e.Struct + "." + e.Field + " of type " + e.Type.String()
+ // The design of UnmarshalTypeError overly assumes a struct-based
+ // Go representation for the JSON value.
+ // The logic in jsontext represents paths using a JSON Pointer,
+ // which is agnostic to the Go type system.
+ // Trying to convert a JSON Pointer into a UnmarshalTypeError.Field
+ // is difficult. As a heuristic, if the last path token looks like
+ // an index into a JSON array (e.g., ".foo.bar.0"),
+ // avoid the phrase "Go struct field ".
+ intoWhat := "Go struct field "
+ i := strings.LastIndexByte(e.Field, '.') + len(".")
+ if len(e.Field[i:]) > 0 && strings.TrimRight(e.Field[i:], "0123456789") == "" {
+ intoWhat = "" // likely a Go slice or array
+ }
+ s = "json: cannot unmarshal " + e.Value + " into " + intoWhat + e.Struct + "." + e.Field + " of type " + e.Type.String()
} else {
s = "json: cannot unmarshal " + e.Value + " into Go value of type " + e.Type.String()
}
}
}
+func TestUnmarshalTypeErrorMessage(t *testing.T) {
+ err := &UnmarshalTypeError{
+ Value: "number 5",
+ Type: reflect.TypeFor[int](),
+ Offset: 1234,
+ Struct: "Root",
+ }
+
+ for _, tt := range []struct {
+ field string
+ want string
+ }{
+ {"", "json: cannot unmarshal number 5 into Go struct field Root. of type int"},
+ {"1", "json: cannot unmarshal number 5 into Root.1 of type int"},
+ {"foo", "json: cannot unmarshal number 5 into Go struct field Root.foo of type int"},
+ {"foo.1", "json: cannot unmarshal number 5 into Root.foo.1 of type int"},
+ {"foo.bar", "json: cannot unmarshal number 5 into Go struct field Root.foo.bar of type int"},
+ {"foo.bar.1", "json: cannot unmarshal number 5 into Root.foo.bar.1 of type int"},
+ {"foo.bar.baz", "json: cannot unmarshal number 5 into Go struct field Root.foo.bar.baz of type int"},
+ } {
+ err.Field = tt.field
+ got := err.Error()
+ if got != tt.want {
+ t.Errorf("Error:\n\tgot: %v\n\twant: %v", got, tt.want)
+ }
+ }
+}
+
func TestUnmarshalSyntax(t *testing.T) {
var x any
tests := []struct {