codekingpro/portable-devtools
114k
1// Copyright 2010 The Go Authors. All rights reserved.2// Use of this source code is governed by a BSD-style3// license that can be found in the LICENSE file.4 5//go:build goexperiment.jsonv26 7package json8 9import (10 "bytes"11 "encoding"12 "errors"13 "fmt"14 "image"15 "io"16 "maps"17 "math"18 "math/big"19 "net"20 "reflect"21 "slices"22 "strconv"23 "strings"24 "testing"25 "time"26)27 28func len64(s string) int64 {29 return int64(len(s))30}31 32type T struct {33 X string34 Y int35 Z int `json:"-"`36}37 38type U struct {39 Alphabet string `json:"alpha"`40}41 42type V struct {43 F1 any44 F2 int3245 F3 Number46 F4 *VOuter47}48 49type VOuter struct {50 V V51}52 53type W struct {54 S SS55}56 57type P struct {58 PP PP59}60 61type PP struct {62 T T63 Ts []T64}65 66type SS string67 68func (*SS) UnmarshalJSON(data []byte) error {69 return &UnmarshalTypeError{Value: "number", Type: reflect.TypeFor[SS]()}70}71 72type TAlias T73 74func (tt *TAlias) UnmarshalJSON(data []byte) error {75 t := T{}76 if err := Unmarshal(data, &t); err != nil {77 return err78 }79 *tt = TAlias(t)80 return nil81}82 83type TOuter struct {84 T TAlias85}86 87// ifaceNumAsFloat64/ifaceNumAsNumber are used to test unmarshaling with and88// without UseNumber89var ifaceNumAsFloat64 = map[string]any{90 "k1": float64(1),91 "k2": "s",92 "k3": []any{float64(1), float64(2.0), float64(3e-3)},93 "k4": map[string]any{"kk1": "s", "kk2": float64(2)},94}95 96var ifaceNumAsNumber = map[string]any{97 "k1": Number("1"),98 "k2": "s",99 "k3": []any{Number("1"), Number("2.0"), Number("3e-3")},100 "k4": map[string]any{"kk1": "s", "kk2": Number("2")},101}102 103type tx struct {104 x int105}106 107type u8 uint8108 109// A type that can unmarshal itself.110 111type unmarshaler struct {112 T bool113}114 115func (u *unmarshaler) UnmarshalJSON(b []byte) error {116 *u = unmarshaler{true} // All we need to see that UnmarshalJSON is called.117 return nil118}119 120type ustruct struct {121 M unmarshaler122}123 124type unmarshalerText struct {125 A, B string126}127 128// needed for re-marshaling tests129func (u unmarshalerText) MarshalText() ([]byte, error) {130 return []byte(u.A + ":" + u.B), nil131}132 133func (u *unmarshalerText) UnmarshalText(b []byte) error {134 pos := bytes.IndexByte(b, ':')135 if pos == -1 {136 return errors.New("missing separator")137 }138 u.A, u.B = string(b[:pos]), string(b[pos+1:])139 return nil140}141 142var _ encoding.TextUnmarshaler = (*unmarshalerText)(nil)143 144type ustructText struct {145 M unmarshalerText146}147 148// u8marshal is an integer type that can marshal/unmarshal itself.149type u8marshal uint8150 151func (u8 u8marshal) MarshalText() ([]byte, error) {152 return []byte(fmt.Sprintf("u%d", u8)), nil153}154 155var errMissingU8Prefix = errors.New("missing 'u' prefix")156 157func (u8 *u8marshal) UnmarshalText(b []byte) error {158 if !bytes.HasPrefix(b, []byte{'u'}) {159 return errMissingU8Prefix160 }161 n, err := strconv.Atoi(string(b[1:]))162 if err != nil {163 return err164 }165 *u8 = u8marshal(n)166 return nil167}168 169var _ encoding.TextUnmarshaler = (*u8marshal)(nil)170 171var (172 umtrue = unmarshaler{true}173 umslice = []unmarshaler{{true}}174 umstruct = ustruct{unmarshaler{true}}175 176 umtrueXY = unmarshalerText{"x", "y"}177 umsliceXY = []unmarshalerText{{"x", "y"}}178 umstructXY = ustructText{unmarshalerText{"x", "y"}}179 180 ummapXY = map[unmarshalerText]bool{{"x", "y"}: true}181)182 183// Test data structures for anonymous fields.184 185type Point struct {186 Z int187}188 189type Top struct {190 Level0 int191 Embed0192 *Embed0a193 *Embed0b `json:"e,omitempty"` // treated as named194 Embed0c `json:"-"` // ignored195 Loop196 Embed0p // has Point with X, Y, used197 Embed0q // has Point with Z, used198 embed // contains exported field199}200 201type Embed0 struct {202 Level1a int // overridden by Embed0a's Level1a with json tag203 Level1b int // used because Embed0a's Level1b is renamed204 Level1c int // used because Embed0a's Level1c is ignored205 Level1d int // annihilated by Embed0a's Level1d206 Level1e int `json:"x"` // annihilated by Embed0a.Level1e207}208 209type Embed0a struct {210 Level1a int `json:"Level1a,omitempty"`211 Level1b int `json:"LEVEL1B,omitempty"`212 Level1c int `json:"-"`213 Level1d int // annihilated by Embed0's Level1d214 Level1f int `json:"x"` // annihilated by Embed0's Level1e215}216 217type Embed0b Embed0218 219type Embed0c Embed0220 221type Embed0p struct {222 image.Point223}224 225type Embed0q struct {226 Point227}228 229type embed struct {230 Q int231}232 233type Loop struct {234 Loop1 int `json:",omitempty"`235 Loop2 int `json:",omitempty"`236 *Loop237}238 239// From reflect test:240// The X in S6 and S7 annihilate, but they also block the X in S8.S9.241type S5 struct {242 S6243 S7244 S8245}246 247type S6 struct {248 X int249}250 251type S7 S6252 253type S8 struct {254 S9255}256 257type S9 struct {258 X int259 Y int260}261 262// From reflect test:263// The X in S11.S6 and S12.S6 annihilate, but they also block the X in S13.S8.S9.264type S10 struct {265 S11266 S12267 S13268}269 270type S11 struct {271 S6272}273 274type S12 struct {275 S6276}277 278type S13 struct {279 S8280}281 282type Ambig struct {283 // Given "hello", the first match should win.284 First int `json:"HELLO"`285 Second int `json:"Hello"`286}287 288type XYZ struct {289 X any290 Y any291 Z any292}293 294type unexportedWithMethods struct{}295 296func (unexportedWithMethods) F() {}297 298type byteWithMarshalJSON byte299 300func (b byteWithMarshalJSON) MarshalJSON() ([]byte, error) {301 return []byte(fmt.Sprintf(`"Z%.2x"`, byte(b))), nil302}303 304func (b *byteWithMarshalJSON) UnmarshalJSON(data []byte) error {305 if len(data) != 5 || data[0] != '"' || data[1] != 'Z' || data[4] != '"' {306 return fmt.Errorf("bad quoted string")307 }308 i, err := strconv.ParseInt(string(data[2:4]), 16, 8)309 if err != nil {310 return fmt.Errorf("bad hex")311 }312 *b = byteWithMarshalJSON(i)313 return nil314}315 316type byteWithPtrMarshalJSON byte317 318func (b *byteWithPtrMarshalJSON) MarshalJSON() ([]byte, error) {319 return byteWithMarshalJSON(*b).MarshalJSON()320}321 322func (b *byteWithPtrMarshalJSON) UnmarshalJSON(data []byte) error {323 return (*byteWithMarshalJSON)(b).UnmarshalJSON(data)324}325 326type byteWithMarshalText byte327 328func (b byteWithMarshalText) MarshalText() ([]byte, error) {329 return []byte(fmt.Sprintf(`Z%.2x`, byte(b))), nil330}331 332func (b *byteWithMarshalText) UnmarshalText(data []byte) error {333 if len(data) != 3 || data[0] != 'Z' {334 return fmt.Errorf("bad quoted string")335 }336 i, err := strconv.ParseInt(string(data[1:3]), 16, 8)337 if err != nil {338 return fmt.Errorf("bad hex")339 }340 *b = byteWithMarshalText(i)341 return nil342}343 344type byteWithPtrMarshalText byte345 346func (b *byteWithPtrMarshalText) MarshalText() ([]byte, error) {347 return byteWithMarshalText(*b).MarshalText()348}349 350func (b *byteWithPtrMarshalText) UnmarshalText(data []byte) error {351 return (*byteWithMarshalText)(b).UnmarshalText(data)352}353 354type intWithMarshalJSON int355 356func (b intWithMarshalJSON) MarshalJSON() ([]byte, error) {357 return []byte(fmt.Sprintf(`"Z%.2x"`, int(b))), nil358}359 360func (b *intWithMarshalJSON) UnmarshalJSON(data []byte) error {361 if len(data) != 5 || data[0] != '"' || data[1] != 'Z' || data[4] != '"' {362 return fmt.Errorf("bad quoted string")363 }364 i, err := strconv.ParseInt(string(data[2:4]), 16, 8)365 if err != nil {366 return fmt.Errorf("bad hex")367 }368 *b = intWithMarshalJSON(i)369 return nil370}371 372type intWithPtrMarshalJSON int373 374func (b *intWithPtrMarshalJSON) MarshalJSON() ([]byte, error) {375 return intWithMarshalJSON(*b).MarshalJSON()376}377 378func (b *intWithPtrMarshalJSON) UnmarshalJSON(data []byte) error {379 return (*intWithMarshalJSON)(b).UnmarshalJSON(data)380}381 382type intWithMarshalText int383 384func (b intWithMarshalText) MarshalText() ([]byte, error) {385 return []byte(fmt.Sprintf(`Z%.2x`, int(b))), nil386}387 388func (b *intWithMarshalText) UnmarshalText(data []byte) error {389 if len(data) != 3 || data[0] != 'Z' {390 return fmt.Errorf("bad quoted string")391 }392 i, err := strconv.ParseInt(string(data[1:3]), 16, 8)393 if err != nil {394 return fmt.Errorf("bad hex")395 }396 *b = intWithMarshalText(i)397 return nil398}399 400type intWithPtrMarshalText int401 402func (b *intWithPtrMarshalText) MarshalText() ([]byte, error) {403 return intWithMarshalText(*b).MarshalText()404}405 406func (b *intWithPtrMarshalText) UnmarshalText(data []byte) error {407 return (*intWithMarshalText)(b).UnmarshalText(data)408}409 410type mapStringToStringData struct {411 Data map[string]string `json:"data"`412}413 414type B struct {415 B bool `json:",string"`416}417 418type DoublePtr struct {419 I **int420 J **int421}422 423type NestedUnamed struct{ F struct{ V int } }424 425var unmarshalTests = []struct {426 CaseName427 in string428 ptr any // new(type)429 out any430 err error431 useNumber bool432 golden bool433 disallowUnknownFields bool434}{435 // basic types436 {CaseName: Name(""), in: `true`, ptr: new(bool), out: true},437 {CaseName: Name(""), in: `1`, ptr: new(int), out: 1},438 {CaseName: Name(""), in: `1.2`, ptr: new(float64), out: 1.2},439 {CaseName: Name(""), in: `-5`, ptr: new(int16), out: int16(-5)},440 {CaseName: Name(""), in: `2`, ptr: new(Number), out: Number("2"), useNumber: true},441 {CaseName: Name(""), in: `2`, ptr: new(Number), out: Number("2")},442 {CaseName: Name(""), in: `2`, ptr: new(any), out: float64(2.0)},443 {CaseName: Name(""), in: `2`, ptr: new(any), out: Number("2"), useNumber: true},444 {CaseName: Name(""), in: `"a\u1234"`, ptr: new(string), out: "a\u1234"},445 {CaseName: Name(""), in: `"http:\/\/"`, ptr: new(string), out: "http://"},446 {CaseName: Name(""), in: `"g-clef: \uD834\uDD1E"`, ptr: new(string), out: "g-clef: \U0001D11E"},447 {CaseName: Name(""), in: `"invalid: \uD834x\uDD1E"`, ptr: new(string), out: "invalid: \uFFFDx\uFFFD"},448 {CaseName: Name(""), in: "null", ptr: new(any), out: nil},449 {CaseName: Name(""), in: `{"X": [1,2,3], "Y": 4}`, ptr: new(T), out: T{Y: 4}, err: &UnmarshalTypeError{"array", reflect.TypeFor[string](), len64(`{"X": `), "T", "X", nil}},450 {CaseName: Name(""), in: `{"X": 23}`, ptr: new(T), out: T{}, err: &UnmarshalTypeError{"number", reflect.TypeFor[string](), len64(`{"X": `), "T", "X", nil}},451 {CaseName: Name(""), in: `{"x": 1}`, ptr: new(tx), out: tx{}},452 {CaseName: Name(""), in: `{"x": 1}`, ptr: new(tx), out: tx{}},453 {CaseName: Name(""), in: `{"x": 1}`, ptr: new(tx), err: fmt.Errorf("json: unknown field \"x\""), disallowUnknownFields: true},454 {CaseName: Name(""), in: `{"S": 23}`, ptr: new(W), out: W{}, err: &UnmarshalTypeError{"number", reflect.TypeFor[SS](), 0, "", "", nil}},455 {CaseName: Name(""), in: `{"T": {"X": 23}}`, ptr: new(TOuter), out: TOuter{}, err: &UnmarshalTypeError{"number", reflect.TypeFor[string](), len64(`{"X": `), "T", "X", nil}},456 {CaseName: Name(""), in: `{"F1":1,"F2":2,"F3":3}`, ptr: new(V), out: V{F1: float64(1), F2: int32(2), F3: Number("3")}},457 {CaseName: Name(""), in: `{"F1":1,"F2":2,"F3":3}`, ptr: new(V), out: V{F1: Number("1"), F2: int32(2), F3: Number("3")}, useNumber: true},458 {CaseName: Name(""), in: `{"k1":1,"k2":"s","k3":[1,2.0,3e-3],"k4":{"kk1":"s","kk2":2}}`, ptr: new(any), out: ifaceNumAsFloat64},459 {CaseName: Name(""), in: `{"k1":1,"k2":"s","k3":[1,2.0,3e-3],"k4":{"kk1":"s","kk2":2}}`, ptr: new(any), out: ifaceNumAsNumber, useNumber: true},460 461 // raw values with whitespace462 {CaseName: Name(""), in: "\n true ", ptr: new(bool), out: true},463 {CaseName: Name(""), in: "\t 1 ", ptr: new(int), out: 1},464 {CaseName: Name(""), in: "\r 1.2 ", ptr: new(float64), out: 1.2},465 {CaseName: Name(""), in: "\t -5 \n", ptr: new(int16), out: int16(-5)},466 {CaseName: Name(""), in: "\t \"a\\u1234\" \n", ptr: new(string), out: "a\u1234"},467 468 // Z has a "-" tag.469 {CaseName: Name(""), in: `{"Y": 1, "Z": 2}`, ptr: new(T), out: T{Y: 1}},470 {CaseName: Name(""), in: `{"Y": 1, "Z": 2}`, ptr: new(T), out: T{Y: 1}, err: fmt.Errorf("json: unknown field \"Z\""), disallowUnknownFields: true},471 472 {CaseName: Name(""), in: `{"alpha": "abc", "alphabet": "xyz"}`, ptr: new(U), out: U{Alphabet: "abc"}},473 {CaseName: Name(""), in: `{"alpha": "abc", "alphabet": "xyz"}`, ptr: new(U), out: U{Alphabet: "abc"}, err: fmt.Errorf("json: unknown field \"alphabet\""), disallowUnknownFields: true},474 {CaseName: Name(""), in: `{"alpha": "abc"}`, ptr: new(U), out: U{Alphabet: "abc"}},475 {CaseName: Name(""), in: `{"alphabet": "xyz"}`, ptr: new(U), out: U{}},476 {CaseName: Name(""), in: `{"alphabet": "xyz"}`, ptr: new(U), err: fmt.Errorf("json: unknown field \"alphabet\""), disallowUnknownFields: true},477 478 // syntax errors479 {CaseName: Name(""), in: ``, ptr: new(any), err: &SyntaxError{errUnexpectedEnd.Error(), 0}},480 {CaseName: Name(""), in: " \n\r\t", ptr: new(any), err: &SyntaxError{errUnexpectedEnd.Error(), len64(" \n\r\t")}},481 {CaseName: Name(""), in: `[2, 3`, ptr: new(any), err: &SyntaxError{errUnexpectedEnd.Error(), len64(`[2, 3`)}},482 {CaseName: Name(""), in: `{"X": "foo", "Y"}`, err: &SyntaxError{"invalid character '}' after object key", len64(`{"X": "foo", "Y"`)}},483 {CaseName: Name(""), in: `[1, 2, 3+]`, err: &SyntaxError{"invalid character '+' after array element", len64(`[1, 2, 3`)}},484 {CaseName: Name(""), in: `{"X":12x}`, err: &SyntaxError{"invalid character 'x' after object key:value pair", len64(`{"X":12`)}, useNumber: true},485 {CaseName: Name(""), in: `{"F3": -}`, ptr: new(V), err: &SyntaxError{"invalid character '}' in numeric literal", len64(`{"F3": -`)}},486 487 // raw value errors488 {CaseName: Name(""), in: "\x01 42", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", len64(``)}},489 {CaseName: Name(""), in: " 42 \x01", err: &SyntaxError{"invalid character '\\x01' after top-level value", len64(` 42 `)}},490 {CaseName: Name(""), in: "\x01 true", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", len64(``)}},491 {CaseName: Name(""), in: " false \x01", err: &SyntaxError{"invalid character '\\x01' after top-level value", len64(` false `)}},492 {CaseName: Name(""), in: "\x01 1.2", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", len64(``)}},493 {CaseName: Name(""), in: " 3.4 \x01", err: &SyntaxError{"invalid character '\\x01' after top-level value", len64(` 3.4 `)}},494 {CaseName: Name(""), in: "\x01 \"string\"", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", len64(``)}},495 {CaseName: Name(""), in: " \"string\" \x01", err: &SyntaxError{"invalid character '\\x01' after top-level value", len64(` "string" `)}},496 497 // array tests498 {CaseName: Name(""), in: `[1, 2, 3]`, ptr: new([3]int), out: [3]int{1, 2, 3}},499 {CaseName: Name(""), in: `[1, 2, 3]`, ptr: new([1]int), out: [1]int{1}},500 {CaseName: Name(""), in: `[1, 2, 3]`, ptr: new([5]int), out: [5]int{1, 2, 3, 0, 0}},501 {CaseName: Name(""), in: `[1, 2, 3]`, ptr: new(MustNotUnmarshalJSON), err: errors.New("MustNotUnmarshalJSON was used")},502 503 // empty array to interface test504 {CaseName: Name(""), in: `[]`, ptr: new([]any), out: []any{}},505 {CaseName: Name(""), in: `null`, ptr: new([]any), out: []any(nil)},506 {CaseName: Name(""), in: `{"T":[]}`, ptr: new(map[string]any), out: map[string]any{"T": []any{}}},507 {CaseName: Name(""), in: `{"T":null}`, ptr: new(map[string]any), out: map[string]any{"T": any(nil)}},508 509 // composite tests510 {CaseName: Name(""), in: allValueIndent, ptr: new(All), out: allValue},511 {CaseName: Name(""), in: allValueCompact, ptr: new(All), out: allValue},512 {CaseName: Name(""), in: allValueIndent, ptr: new(*All), out: &allValue},513 {CaseName: Name(""), in: allValueCompact, ptr: new(*All), out: &allValue},514 {CaseName: Name(""), in: pallValueIndent, ptr: new(All), out: pallValue},515 {CaseName: Name(""), in: pallValueCompact, ptr: new(All), out: pallValue},516 {CaseName: Name(""), in: pallValueIndent, ptr: new(*All), out: &pallValue},517 {CaseName: Name(""), in: pallValueCompact, ptr: new(*All), out: &pallValue},518 519 // unmarshal interface test520 {CaseName: Name(""), in: `{"T":false}`, ptr: new(unmarshaler), out: umtrue}, // use "false" so test will fail if custom unmarshaler is not called521 {CaseName: Name(""), in: `{"T":false}`, ptr: new(*unmarshaler), out: &umtrue},522 {CaseName: Name(""), in: `[{"T":false}]`, ptr: new([]unmarshaler), out: umslice},523 {CaseName: Name(""), in: `[{"T":false}]`, ptr: new(*[]unmarshaler), out: &umslice},524 {CaseName: Name(""), in: `{"M":{"T":"x:y"}}`, ptr: new(ustruct), out: umstruct},525 526 // UnmarshalText interface test527 {CaseName: Name(""), in: `"x:y"`, ptr: new(unmarshalerText), out: umtrueXY},528 {CaseName: Name(""), in: `"x:y"`, ptr: new(*unmarshalerText), out: &umtrueXY},529 {CaseName: Name(""), in: `["x:y"]`, ptr: new([]unmarshalerText), out: umsliceXY},530 {CaseName: Name(""), in: `["x:y"]`, ptr: new(*[]unmarshalerText), out: &umsliceXY},531 {CaseName: Name(""), in: `{"M":"x:y"}`, ptr: new(ustructText), out: umstructXY},532 533 // integer-keyed map test534 {535 CaseName: Name(""),536 in: `{"-1":"a","0":"b","1":"c"}`,537 ptr: new(map[int]string),538 out: map[int]string{-1: "a", 0: "b", 1: "c"},539 },540 {541 CaseName: Name(""),542 in: `{"0":"a","10":"c","9":"b"}`,543 ptr: new(map[u8]string),544 out: map[u8]string{0: "a", 9: "b", 10: "c"},545 },546 {547 CaseName: Name(""),548 in: `{"-9223372036854775808":"min","9223372036854775807":"max"}`,549 ptr: new(map[int64]string),550 out: map[int64]string{math.MinInt64: "min", math.MaxInt64: "max"},551 },552 {553 CaseName: Name(""),554 in: `{"18446744073709551615":"max"}`,555 ptr: new(map[uint64]string),556 out: map[uint64]string{math.MaxUint64: "max"},557 },558 {559 CaseName: Name(""),560 in: `{"0":false,"10":true}`,561 ptr: new(map[uintptr]bool),562 out: map[uintptr]bool{0: false, 10: true},563 },564 565 // Check that MarshalText and UnmarshalText take precedence566 // over default integer handling in map keys.567 {568 CaseName: Name(""),569 in: `{"u2":4}`,570 ptr: new(map[u8marshal]int),571 out: map[u8marshal]int{2: 4},572 },573 {574 CaseName: Name(""),575 in: `{"2":4}`,576 ptr: new(map[u8marshal]int),577 out: map[u8marshal]int{},578 err: errMissingU8Prefix,579 },580 581 // integer-keyed map errors582 {583 CaseName: Name(""),584 in: `{"abc":"abc"}`,585 ptr: new(map[int]string),586 out: map[int]string{},587 err: &UnmarshalTypeError{Value: "number abc", Type: reflect.TypeFor[int](), Field: "abc", Offset: len64(`{`)},588 },589 {590 CaseName: Name(""),591 in: `{"256":"abc"}`,592 ptr: new(map[uint8]string),593 out: map[uint8]string{},594 err: &UnmarshalTypeError{Value: "number 256", Type: reflect.TypeFor[uint8](), Field: "256", Offset: len64(`{`)},595 },596 {597 CaseName: Name(""),598 in: `{"128":"abc"}`,599 ptr: new(map[int8]string),600 out: map[int8]string{},601 err: &UnmarshalTypeError{Value: "number 128", Type: reflect.TypeFor[int8](), Field: "128", Offset: len64(`{`)},602 },603 {604 CaseName: Name(""),605 in: `{"-1":"abc"}`,606 ptr: new(map[uint8]string),607 out: map[uint8]string{},608 err: &UnmarshalTypeError{Value: "number -1", Type: reflect.TypeFor[uint8](), Field: "-1", Offset: len64(`{`)},609 },610 {611 CaseName: Name(""),612 in: `{"F":{"a":2,"3":4}}`,613 ptr: new(map[string]map[int]int),614 out: map[string]map[int]int{"F": {3: 4}},615 err: &UnmarshalTypeError{Value: "number a", Type: reflect.TypeFor[int](), Field: "F.a", Offset: len64(`{"F":{`)},616 },617 {618 CaseName: Name(""),619 in: `{"F":{"a":2,"3":4}}`,620 ptr: new(map[string]map[uint]int),621 out: map[string]map[uint]int{"F": {3: 4}},622 err: &UnmarshalTypeError{Value: "number a", Type: reflect.TypeFor[uint](), Field: "F.a", Offset: len64(`{"F":{`)},623 },624 625 // Map keys can be encoding.TextUnmarshalers.626 {CaseName: Name(""), in: `{"x:y":true}`, ptr: new(map[unmarshalerText]bool), out: ummapXY},627 // If multiple values for the same key exists, only the most recent value is used.628 {CaseName: Name(""), in: `{"x:y":false,"x:y":true}`, ptr: new(map[unmarshalerText]bool), out: ummapXY},629 630 {631 CaseName: Name(""),632 in: `{633 "Level0": 1,634 "Level1b": 2,635 "Level1c": 3,636 "x": 4,637 "Level1a": 5,638 "LEVEL1B": 6,639 "e": {640 "Level1a": 8,641 "Level1b": 9,642 "Level1c": 10,643 "Level1d": 11,644 "x": 12645 },646 "Loop1": 13,647 "Loop2": 14,648 "X": 15,649 "Y": 16,650 "Z": 17,651 "Q": 18652 }`,653 ptr: new(Top),654 out: Top{655 Level0: 1,656 Embed0: Embed0{657 Level1b: 2,658 Level1c: 3,659 },660 Embed0a: &Embed0a{661 Level1a: 5,662 Level1b: 6,663 },664 Embed0b: &Embed0b{665 Level1a: 8,666 Level1b: 9,667 Level1c: 10,668 Level1d: 11,669 Level1e: 12,670 },671 Loop: Loop{672 Loop1: 13,673 Loop2: 14,674 },675 Embed0p: Embed0p{676 Point: image.Point{X: 15, Y: 16},677 },678 Embed0q: Embed0q{679 Point: Point{Z: 17},680 },681 embed: embed{682 Q: 18,683 },684 },685 },686 {687 CaseName: Name(""),688 in: `{"hello": 1}`,689 ptr: new(Ambig),690 out: Ambig{First: 1},691 },692 693 {694 CaseName: Name(""),695 in: `{"X": 1,"Y":2}`,696 ptr: new(S5),697 out: S5{S8: S8{S9: S9{Y: 2}}},698 },699 {700 CaseName: Name(""),701 in: `{"X": 1,"Y":2}`,702 ptr: new(S5),703 out: S5{S8: S8{S9{Y: 2}}},704 err: fmt.Errorf("json: unknown field \"X\""),705 disallowUnknownFields: true,706 },707 {708 CaseName: Name(""),709 in: `{"X": 1,"Y":2}`,710 ptr: new(S10),711 out: S10{S13: S13{S8: S8{S9: S9{Y: 2}}}},712 },713 {714 CaseName: Name(""),715 in: `{"X": 1,"Y":2}`,716 ptr: new(S10),717 out: S10{S13: S13{S8{S9{Y: 2}}}},718 err: fmt.Errorf("json: unknown field \"X\""),719 disallowUnknownFields: true,720 },721 {722 CaseName: Name(""),723 in: `{"I": 0, "I": null, "J": null}`,724 ptr: new(DoublePtr),725 out: DoublePtr{I: nil, J: nil},726 },727 728 // invalid UTF-8 is coerced to valid UTF-8.729 {730 CaseName: Name(""),731 in: "\"hello\xffworld\"",732 ptr: new(string),733 out: "hello\ufffdworld",734 },735 {736 CaseName: Name(""),737 in: "\"hello\xc2\xc2world\"",738 ptr: new(string),739 out: "hello\ufffd\ufffdworld",740 },741 {742 CaseName: Name(""),743 in: "\"hello\xc2\xffworld\"",744 ptr: new(string),745 out: "hello\ufffd\ufffdworld",746 },747 {748 CaseName: Name(""),749 in: "\"hello\\ud800world\"",750 ptr: new(string),751 out: "hello\ufffdworld",752 },753 {754 CaseName: Name(""),755 in: "\"hello\\ud800\\ud800world\"",756 ptr: new(string),757 out: "hello\ufffd\ufffdworld",758 },759 {760 CaseName: Name(""),761 in: "\"hello\\ud800\\ud800world\"",762 ptr: new(string),763 out: "hello\ufffd\ufffdworld",764 },765 {766 CaseName: Name(""),767 in: "\"hello\xed\xa0\x80\xed\xb0\x80world\"",768 ptr: new(string),769 out: "hello\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdworld",770 },771 772 // Used to be issue 8305, but time.Time implements encoding.TextUnmarshaler so this works now.773 {774 CaseName: Name(""),775 in: `{"2009-11-10T23:00:00Z": "hello world"}`,776 ptr: new(map[time.Time]string),777 out: map[time.Time]string{time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC): "hello world"},778 },779 780 // issue 8305781 {782 CaseName: Name(""),783 in: `{"2009-11-10T23:00:00Z": "hello world"}`,784 ptr: new(map[Point]string),785 out: map[Point]string{},786 err: &UnmarshalTypeError{Value: "string", Type: reflect.TypeFor[Point](), Field: `2009-11-10T23:00:00Z`, Offset: len64(`{`)},787 },788 {789 CaseName: Name(""),790 in: `{"asdf": "hello world"}`,791 ptr: new(map[unmarshaler]string),792 out: map[unmarshaler]string{},793 err: &UnmarshalTypeError{Value: "string", Type: reflect.TypeFor[unmarshaler](), Field: "asdf", Offset: len64(`{`)},794 },795 796 // related to issue 13783.797 // Go 1.7 changed marshaling a slice of typed byte to use the methods on the byte type,798 // similar to marshaling a slice of typed int.799 // These tests check that, assuming the byte type also has valid decoding methods,800 // either the old base64 string encoding or the new per-element encoding can be801 // successfully unmarshaled. The custom unmarshalers were accessible in earlier802 // versions of Go, even though the custom marshaler was not.803 {804 CaseName: Name(""),805 in: `"AQID"`,806 ptr: new([]byteWithMarshalJSON),807 out: []byteWithMarshalJSON{1, 2, 3},808 },809 {810 CaseName: Name(""),811 in: `["Z01","Z02","Z03"]`,812 ptr: new([]byteWithMarshalJSON),813 out: []byteWithMarshalJSON{1, 2, 3},814 golden: true,815 },816 {817 CaseName: Name(""),818 in: `"AQID"`,819 ptr: new([]byteWithMarshalText),820 out: []byteWithMarshalText{1, 2, 3},821 },822 {823 CaseName: Name(""),824 in: `["Z01","Z02","Z03"]`,825 ptr: new([]byteWithMarshalText),826 out: []byteWithMarshalText{1, 2, 3},827 golden: true,828 },829 {830 CaseName: Name(""),831 in: `"AQID"`,832 ptr: new([]byteWithPtrMarshalJSON),833 out: []byteWithPtrMarshalJSON{1, 2, 3},834 },835 {836 CaseName: Name(""),837 in: `["Z01","Z02","Z03"]`,838 ptr: new([]byteWithPtrMarshalJSON),839 out: []byteWithPtrMarshalJSON{1, 2, 3},840 golden: true,841 },842 {843 CaseName: Name(""),844 in: `"AQID"`,845 ptr: new([]byteWithPtrMarshalText),846 out: []byteWithPtrMarshalText{1, 2, 3},847 },848 {849 CaseName: Name(""),850 in: `["Z01","Z02","Z03"]`,851 ptr: new([]byteWithPtrMarshalText),852 out: []byteWithPtrMarshalText{1, 2, 3},853 golden: true,854 },855 856 // ints work with the marshaler but not the base64 []byte case857 {858 CaseName: Name(""),859 in: `["Z01","Z02","Z03"]`,860 ptr: new([]intWithMarshalJSON),861 out: []intWithMarshalJSON{1, 2, 3},862 golden: true,863 },864 {865 CaseName: Name(""),866 in: `["Z01","Z02","Z03"]`,867 ptr: new([]intWithMarshalText),868 out: []intWithMarshalText{1, 2, 3},869 golden: true,870 },871 {872 CaseName: Name(""),873 in: `["Z01","Z02","Z03"]`,874 ptr: new([]intWithPtrMarshalJSON),875 out: []intWithPtrMarshalJSON{1, 2, 3},876 golden: true,877 },878 {879 CaseName: Name(""),880 in: `["Z01","Z02","Z03"]`,881 ptr: new([]intWithPtrMarshalText),882 out: []intWithPtrMarshalText{1, 2, 3},883 golden: true,884 },885 886 {CaseName: Name(""), in: `0.000001`, ptr: new(float64), out: 0.000001, golden: true},887 {CaseName: Name(""), in: `1e-7`, ptr: new(float64), out: 1e-7, golden: true},888 {CaseName: Name(""), in: `100000000000000000000`, ptr: new(float64), out: 100000000000000000000.0, golden: true},889 {CaseName: Name(""), in: `1e+21`, ptr: new(float64), out: 1e21, golden: true},890 {CaseName: Name(""), in: `-0.000001`, ptr: new(float64), out: -0.000001, golden: true},891 {CaseName: Name(""), in: `-1e-7`, ptr: new(float64), out: -1e-7, golden: true},892 {CaseName: Name(""), in: `-100000000000000000000`, ptr: new(float64), out: -100000000000000000000.0, golden: true},893 {CaseName: Name(""), in: `-1e+21`, ptr: new(float64), out: -1e21, golden: true},894 {CaseName: Name(""), in: `999999999999999900000`, ptr: new(float64), out: 999999999999999900000.0, golden: true},895 {CaseName: Name(""), in: `9007199254740992`, ptr: new(float64), out: 9007199254740992.0, golden: true},896 {CaseName: Name(""), in: `9007199254740993`, ptr: new(float64), out: 9007199254740992.0, golden: false},897 898 {899 CaseName: Name(""),900 in: `{"V": {"F2": "hello"}}`,901 ptr: new(VOuter),902 err: &UnmarshalTypeError{903 Value: "string",904 Struct: "VOuter",905 Field: "V.F2",906 Type: reflect.TypeFor[int32](),907 Offset: len64(`{"V": {"F2": `),908 },909 },910 {911 CaseName: Name(""),912 in: `{"V": {"F4": {}, "F2": "hello"}}`,913 ptr: new(VOuter),914 out: VOuter{V: V{F4: &VOuter{}}},915 err: &UnmarshalTypeError{916 Value: "string",917 Struct: "VOuter",918 Field: "V.F2",919 Type: reflect.TypeFor[int32](),920 Offset: len64(`{"V": {"F4": {}, "F2": `),921 },922 },923 924 {925 CaseName: Name(""),926 in: `{"Level1a": "hello"}`,927 ptr: new(Top),928 out: Top{Embed0a: &Embed0a{}},929 err: &UnmarshalTypeError{930 Value: "string",931 Struct: "Top",932 Field: "Level1a",933 Type: reflect.TypeFor[int](),934 Offset: len64(`{"Level1a": `),935 },936 },937 938 // issue 15146.939 // invalid inputs in wrongStringTests below.940 {CaseName: Name(""), in: `{"B":"true"}`, ptr: new(B), out: B{true}, golden: true},941 {CaseName: Name(""), in: `{"B":"false"}`, ptr: new(B), out: B{false}, golden: true},942 {CaseName: Name(""), in: `{"B": "maybe"}`, ptr: new(B), err: &UnmarshalTypeError{Value: `string "maybe"`, Type: reflect.TypeFor[bool](), Struct: "B", Field: "B", Offset: len64(`{"B": `), Err: strconv.ErrSyntax}},943 {CaseName: Name(""), in: `{"B": "tru"}`, ptr: new(B), err: &UnmarshalTypeError{Value: `string "tru"`, Type: reflect.TypeFor[bool](), Struct: "B", Field: "B", Offset: len64(`{"B": `), Err: strconv.ErrSyntax}},944 {CaseName: Name(""), in: `{"B": "False"}`, ptr: new(B), err: &UnmarshalTypeError{Value: `string "False"`, Type: reflect.TypeFor[bool](), Struct: "B", Field: "B", Offset: len64(`{"B": `), Err: strconv.ErrSyntax}},945 {CaseName: Name(""), in: `{"B": "null"}`, ptr: new(B), out: B{false}},946 {CaseName: Name(""), in: `{"B": "nul"}`, ptr: new(B), err: &UnmarshalTypeError{Value: `string "nul"`, Type: reflect.TypeFor[bool](), Struct: "B", Field: "B", Offset: len64(`{"B": `), Err: strconv.ErrSyntax}},947 {CaseName: Name(""), in: `{"B": [2, 3]}`, ptr: new(B), err: &UnmarshalTypeError{Value: "array", Type: reflect.TypeFor[bool](), Struct: "B", Field: "B", Offset: len64(`{"B": `)}},948 949 // additional tests for disallowUnknownFields950 {951 CaseName: Name(""),952 in: `{953 "Level0": 1,954 "Level1b": 2,955 "Level1c": 3,956 "x": 4,957 "Level1a": 5,958 "LEVEL1B": 6,959 "e": {960 "Level1a": 8,961 "Level1b": 9,962 "Level1c": 10,963 "Level1d": 11,964 "x": 12965 },966 "Loop1": 13,967 "Loop2": 14,968 "X": 15,969 "Y": 16,970 "Z": 17,971 "Q": 18,972 "extra": true973 }`,974 ptr: new(Top),975 out: Top{976 Level0: 1,977 Embed0: Embed0{978 Level1b: 2,979 Level1c: 3,980 },981 Embed0a: &Embed0a{Level1a: 5, Level1b: 6},982 Embed0b: &Embed0b{Level1a: 8, Level1b: 9, Level1c: 10, Level1d: 11, Level1e: 12},983 Loop: Loop{984 Loop1: 13,985 Loop2: 14,986 Loop: nil,987 },988 Embed0p: Embed0p{989 Point: image.Point{990 X: 15,991 Y: 16,992 },993 },994 Embed0q: Embed0q{Point: Point{Z: 17}},995 embed: embed{Q: 18},996 },997 err: fmt.Errorf("json: unknown field \"extra\""),998 disallowUnknownFields: true,999 },1000 {1001 CaseName: Name(""),1002 in: `{1003 "Level0": 1,1004 "Level1b": 2,1005 "Level1c": 3,1006 "x": 4,1007 "Level1a": 5,1008 "LEVEL1B": 6,1009 "e": {1010 "Level1a": 8,1011 "Level1b": 9,1012 "Level1c": 10,1013 "Level1d": 11,1014 "x": 12,1015 "extra": null1016 },1017 "Loop1": 13,1018 "Loop2": 14,1019 "X": 15,1020 "Y": 16,1021 "Z": 17,1022 "Q": 181023 }`,1024 ptr: new(Top),1025 out: Top{1026 Level0: 1,1027 Embed0: Embed0{1028 Level1b: 2,1029 Level1c: 3,1030 },1031 Embed0a: &Embed0a{Level1a: 5, Level1b: 6},1032 Embed0b: &Embed0b{Level1a: 8, Level1b: 9, Level1c: 10, Level1d: 11, Level1e: 12},1033 Loop: Loop{1034 Loop1: 13,1035 Loop2: 14,1036 Loop: nil,1037 },1038 Embed0p: Embed0p{1039 Point: image.Point{1040 X: 15,1041 Y: 16,1042 },1043 },1044 Embed0q: Embed0q{Point: Point{Z: 17}},1045 embed: embed{Q: 18},1046 },1047 err: fmt.Errorf("json: unknown field \"extra\""),1048 disallowUnknownFields: true,1049 },1050 // issue 264441051 // UnmarshalTypeError without field & struct values1052 {1053 CaseName: Name(""),1054 in: `{"data":{"test1": "bob", "test2": 123}}`,1055 ptr: new(mapStringToStringData),1056 out: mapStringToStringData{map[string]string{"test1": "bob", "test2": ""}},1057 err: &UnmarshalTypeError{Value: "number", Type: reflect.TypeFor[string](), Offset: len64(`{"data":{"test1": "bob", "test2": `), Struct: "mapStringToStringData", Field: "data.test2"},1058 },1059 {1060 CaseName: Name(""),1061 in: `{"data":{"test1": 123, "test2": "bob"}}`,1062 ptr: new(mapStringToStringData),1063 out: mapStringToStringData{Data: map[string]string{"test1": "", "test2": "bob"}},1064 err: &UnmarshalTypeError{Value: "number", Type: reflect.TypeFor[string](), Offset: len64(`{"data":{"test1": `), Struct: "mapStringToStringData", Field: "data.test1"},1065 },1066 1067 // trying to decode JSON arrays or objects via TextUnmarshaler1068 {1069 CaseName: Name(""),1070 in: `[1, 2, 3]`,1071 ptr: new(MustNotUnmarshalText),1072 err: &UnmarshalTypeError{Value: "array", Type: reflect.TypeFor[MustNotUnmarshalText](), Err: errors.New("JSON value must be string type")},1073 },1074 {1075 CaseName: Name(""),1076 in: `{"foo": "bar"}`,1077 ptr: new(MustNotUnmarshalText),1078 err: &UnmarshalTypeError{Value: "object", Type: reflect.TypeFor[MustNotUnmarshalText](), Err: errors.New("JSON value must be string type")},1079 },1080 // #223691081 {1082 CaseName: Name(""),1083 in: `{"PP": {"T": {"Y": "bad-type"}}}`,1084 ptr: new(P),1085 err: &UnmarshalTypeError{1086 Value: "string",1087 Struct: "P",1088 Field: "PP.T.Y",1089 Type: reflect.TypeFor[int](),1090 Offset: len64(`{"PP": {"T": {"Y": `),1091 },1092 },1093 {1094 CaseName: Name(""),1095 in: `{"Ts": [{"Y": 1}, {"Y": 2}, {"Y": "bad-type"}]}`,1096 ptr: new(PP),1097 out: PP{Ts: []T{{Y: 1}, {Y: 2}, {Y: 0}}},1098 err: &UnmarshalTypeError{1099 Value: "string",1100 Struct: "PP",1101 Field: "Ts.2.Y",1102 Type: reflect.TypeFor[int](),1103 Offset: len64(`{"Ts": [{"Y": 1}, {"Y": 2}, {"Y": `),1104 },1105 },1106 // #147021107 {1108 CaseName: Name(""),1109 in: `invalid`,1110 ptr: new(Number),1111 err: &SyntaxError{1112 msg: "invalid character 'i' looking for beginning of value",1113 Offset: len64(``),1114 },1115 },1116 {1117 CaseName: Name(""),1118 in: `"invalid"`,1119 ptr: new(Number),1120 err: &UnmarshalTypeError{Value: `string "invalid"`, Type: reflect.TypeFor[Number](), Err: strconv.ErrSyntax},1121 },1122 {1123 CaseName: Name(""),1124 in: `{"A":"invalid"}`,1125 ptr: new(struct{ A Number }),1126 err: &UnmarshalTypeError{Value: `string "invalid"`, Type: reflect.TypeFor[Number](), Err: strconv.ErrSyntax},1127 },1128 {1129 CaseName: Name(""),1130 in: `{"A":"invalid"}`,1131 ptr: new(struct {1132 A Number `json:",string"`1133 }),1134 err: &UnmarshalTypeError{Value: `string "invalid"`, Type: reflect.TypeFor[Number](), Err: strconv.ErrSyntax},1135 },1136 {1137 CaseName: Name(""),1138 in: `{"A":"invalid"}`,1139 ptr: new(map[string]Number),1140 out: map[string]Number{"A": ""},1141 err: &UnmarshalTypeError{Value: `string "invalid"`, Type: reflect.TypeFor[Number](), Err: strconv.ErrSyntax},1142 },1143 1144 {1145 CaseName: Name(""),1146 in: `5`,1147 ptr: new(Number),1148 out: Number("5"),1149 },1150 {1151 CaseName: Name(""),1152 in: `"5"`,1153 ptr: new(Number),1154 out: Number("5"),1155 },1156 {1157 CaseName: Name(""),1158 in: `{"N":5}`,1159 ptr: new(struct{ N Number }),1160 out: struct{ N Number }{"5"},1161 },1162 {1163 CaseName: Name(""),1164 in: `{"N":"5"}`,1165 ptr: new(struct{ N Number }),1166 out: struct{ N Number }{"5"},1167 },1168 {1169 CaseName: Name(""),1170 in: `{"N":5}`,1171 ptr: new(struct {1172 N Number `json:",string"`1173 }),1174 err: &UnmarshalTypeError{Value: "number", Type: reflect.TypeFor[Number]()},1175 },1176 {1177 CaseName: Name(""),1178 in: `{"N":"5"}`,1179 ptr: new(struct {1180 N Number `json:",string"`1181 }),1182 out: struct {1183 N Number `json:",string"`1184 }{"5"},1185 },1186 1187 // Verify that syntactic errors are immediately fatal,1188 // while semantic errors are lazily reported1189 // (i.e., allow processing to continue).1190 {1191 CaseName: Name(""),1192 in: `[1,2,true,4,5}`,1193 ptr: new([]int),1194 err: &SyntaxError{msg: "invalid character '}' after array element", Offset: len64(`[1,2,true,4,5`)},1195 },1196 {1197 CaseName: Name(""),1198 in: `[1,2,true,4,5]`,1199 ptr: new([]int),1200 out: []int{1, 2, 0, 4, 5},