k8s.io/kube-openapi@v0.0.0-20240228011516-70dd3763d340/pkg/internal/third_party/go-json-experiment/json/diff_test.go (about)

     1  // Copyright 2020 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package json_test
     6  
     7  import (
     8  	"errors"
     9  	"math"
    10  	"path"
    11  	"reflect"
    12  	"strings"
    13  	"testing"
    14  	"time"
    15  
    16  	jsonv1 "encoding/json"
    17  
    18  	jsonv2 "k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json"
    19  )
    20  
    21  // NOTE: This file serves as a list of semantic differences between v1 and v2.
    22  // Each test explains how v1 behaves, how v2 behaves, and
    23  // a rationale for why the behavior was changed.
    24  
    25  var jsonPackages = []struct {
    26  	Version   string
    27  	Marshal   func(any) ([]byte, error)
    28  	Unmarshal func([]byte, any) error
    29  }{
    30  	{"v1", jsonv1.Marshal, jsonv1.Unmarshal},
    31  	{"v2", jsonv2.Marshal, jsonv2.Unmarshal},
    32  }
    33  
    34  // In v1, unmarshal matches struct fields using a case-insensitive match.
    35  // In v2, unmarshal matches struct fields using a case-sensitive match.
    36  //
    37  // Case-insensitive matching is a surprising default and
    38  // incurs significant performance cost when unmarshaling unknown fields.
    39  // In v2, we can opt into v1-like behavior with the `nocase` tag option.
    40  // The case-insensitive matching performed by v2 is looser than that of v1
    41  // where it also ignores dashes and underscores.
    42  // This allows v2 to match fields regardless of whether the name is in
    43  // snake_case, camelCase, or kebab-case.
    44  //
    45  // Related issue:
    46  //
    47  //	https://go.dev/issue/14750
    48  func TestCaseSensitivity(t *testing.T) {
    49  	type Fields struct {
    50  		FieldA bool
    51  		FieldB bool `json:"fooBar"`
    52  		FieldC bool `json:"fizzBuzz,nocase"` // `nocase` is used by v2 to explicitly enable case-insensitive matching
    53  	}
    54  
    55  	for _, json := range jsonPackages {
    56  		t.Run(path.Join("Unmarshal", json.Version), func(t *testing.T) {
    57  			// This is a mapping from Go field names to JSON member names to
    58  			// whether the JSON member name would match the Go field name.
    59  			type goName = string
    60  			type jsonName = string
    61  			onlyV1 := json.Version == "v1"
    62  			onlyV2 := json.Version == "v2"
    63  			allMatches := map[goName]map[jsonName]bool{
    64  				"FieldA": {
    65  					"FieldA": true,   // exact match
    66  					"fielda": onlyV1, // v1 is case-insensitive by default
    67  					"fieldA": onlyV1, // v1 is case-insensitive by default
    68  					"FIELDA": onlyV1, // v1 is case-insensitive by default
    69  					"FieldB": false,
    70  					"FieldC": false,
    71  				},
    72  				"FieldB": {
    73  					"fooBar":   true,   // exact match for explicitly specified JSON name
    74  					"FooBar":   onlyV1, // v1 is case-insensitive even if an explicit JSON name is provided
    75  					"foobar":   onlyV1, // v1 is case-insensitive even if an explicit JSON name is provided
    76  					"FOOBAR":   onlyV1, // v1 is case-insensitive even if an explicit JSON name is provided
    77  					"fizzBuzz": false,
    78  					"FieldA":   false,
    79  					"FieldB":   false, // explicit JSON name means that the Go field name is not used for matching
    80  					"FieldC":   false,
    81  				},
    82  				"FieldC": {
    83  					"fizzBuzz":  true,   // exact match for explicitly specified JSON name
    84  					"fizzbuzz":  true,   // v2 is case-insensitive due to `nocase` tag
    85  					"FIZZBUZZ":  true,   // v2 is case-insensitive due to `nocase` tag
    86  					"fizz_buzz": onlyV2, // case-insensitivity in v2 ignores dashes and underscores
    87  					"fizz-buzz": onlyV2, // case-insensitivity in v2 ignores dashes and underscores
    88  					"fooBar":    false,
    89  					"FieldA":    false,
    90  					"FieldC":    false, // explicit JSON name means that the Go field name is not used for matching
    91  					"FieldB":    false,
    92  				},
    93  			}
    94  
    95  			for goFieldName, matches := range allMatches {
    96  				for jsonMemberName, wantMatch := range matches {
    97  					in := `{"` + jsonMemberName + `":true}`
    98  					var s Fields
    99  					if err := json.Unmarshal([]byte(in), &s); err != nil {
   100  						t.Fatalf("json.Unmarshal error: %v", err)
   101  					}
   102  					gotMatch := reflect.ValueOf(s).FieldByName(goFieldName).Bool()
   103  					if gotMatch != wantMatch {
   104  						t.Fatalf("%T.%s = %v, want %v", s, goFieldName, gotMatch, wantMatch)
   105  					}
   106  				}
   107  			}
   108  		})
   109  	}
   110  }
   111  
   112  // In v1, the "omitempty" option specifies that a struct field is omitted
   113  // when marshaling if it is an empty Go value, which is defined as
   114  // false, 0, a nil pointer, a nil interface value, and
   115  // any empty array, slice, map, or string.
   116  //
   117  // In v2, the "omitempty" option specifies that a struct field is omitted
   118  // when marshaling if it is an empty JSON value, which is defined as
   119  // a JSON null or empty JSON string, object, or array.
   120  //
   121  // In v2, we also provide the "omitzero" option which specifies that a field
   122  // is omitted if it is the zero Go value or if it implements an "IsZero() bool"
   123  // method that reports true. Together, "omitzero" and "omitempty" can cover
   124  // all the prior use cases of the v1 definition of "omitempty".
   125  // Note that "omitempty" is defined in terms of the Go type system in v1,
   126  // but now defined in terms of the JSON type system in v2.
   127  //
   128  // Related issues:
   129  //
   130  //	https://go.dev/issue/11939
   131  //	https://go.dev/issue/22480
   132  //	https://go.dev/issue/29310
   133  //	https://go.dev/issue/32675
   134  //	https://go.dev/issue/45669
   135  //	https://go.dev/issue/45787
   136  //	https://go.dev/issue/50480
   137  //	https://go.dev/issue/52803
   138  func TestOmitEmptyOption(t *testing.T) {
   139  	type Struct struct {
   140  		Foo string  `json:",omitempty"`
   141  		Bar []int   `json:",omitempty"`
   142  		Baz *Struct `json:",omitempty"`
   143  	}
   144  	type Types struct {
   145  		Bool       bool              `json:",omitempty"`
   146  		StringA    string            `json:",omitempty"`
   147  		StringB    string            `json:",omitempty"`
   148  		BytesA     []byte            `json:",omitempty"`
   149  		BytesB     []byte            `json:",omitempty"`
   150  		BytesC     []byte            `json:",omitempty"`
   151  		Int        int               `json:",omitempty"`
   152  		MapA       map[string]string `json:",omitempty"`
   153  		MapB       map[string]string `json:",omitempty"`
   154  		MapC       map[string]string `json:",omitempty"`
   155  		StructA    Struct            `json:",omitempty"`
   156  		StructB    Struct            `json:",omitempty"`
   157  		StructC    Struct            `json:",omitempty"`
   158  		SliceA     []string          `json:",omitempty"`
   159  		SliceB     []string          `json:",omitempty"`
   160  		SliceC     []string          `json:",omitempty"`
   161  		Array      [1]string         `json:",omitempty"`
   162  		PointerA   *string           `json:",omitempty"`
   163  		PointerB   *string           `json:",omitempty"`
   164  		PointerC   *string           `json:",omitempty"`
   165  		InterfaceA any               `json:",omitempty"`
   166  		InterfaceB any               `json:",omitempty"`
   167  		InterfaceC any               `json:",omitempty"`
   168  		InterfaceD any               `json:",omitempty"`
   169  	}
   170  
   171  	something := "something"
   172  	for _, json := range jsonPackages {
   173  		t.Run(path.Join("Marshal", json.Version), func(t *testing.T) {
   174  			in := Types{
   175  				Bool:       false,
   176  				StringA:    "",
   177  				StringB:    something,
   178  				BytesA:     nil,
   179  				BytesB:     []byte{},
   180  				BytesC:     []byte(something),
   181  				Int:        0,
   182  				MapA:       nil,
   183  				MapB:       map[string]string{},
   184  				MapC:       map[string]string{something: something},
   185  				StructA:    Struct{},
   186  				StructB:    Struct{Bar: []int{}, Baz: new(Struct)},
   187  				StructC:    Struct{Foo: something},
   188  				SliceA:     nil,
   189  				SliceB:     []string{},
   190  				SliceC:     []string{something},
   191  				Array:      [1]string{something},
   192  				PointerA:   nil,
   193  				PointerB:   new(string),
   194  				PointerC:   &something,
   195  				InterfaceA: nil,
   196  				InterfaceB: (*string)(nil),
   197  				InterfaceC: new(string),
   198  				InterfaceD: &something,
   199  			}
   200  			b, err := json.Marshal(in)
   201  			if err != nil {
   202  				t.Fatalf("json.Marshal error: %v", err)
   203  			}
   204  			var out map[string]any
   205  			if err := json.Unmarshal(b, &out); err != nil {
   206  				t.Fatalf("json.Unmarshal error: %v", err)
   207  			}
   208  
   209  			onlyV1 := json.Version == "v1"
   210  			onlyV2 := json.Version == "v2"
   211  			wantPresent := map[string]bool{
   212  				"Bool":       onlyV2, // false is an empty Go bool, but is NOT an empty JSON value
   213  				"StringA":    false,
   214  				"StringB":    true,
   215  				"BytesA":     false,
   216  				"BytesB":     false,
   217  				"BytesC":     true,
   218  				"Int":        onlyV2, // 0 is an empty Go integer, but NOT an empty JSON value
   219  				"MapA":       false,
   220  				"MapB":       false,
   221  				"MapC":       true,
   222  				"StructA":    onlyV1, // Struct{} is NOT an empty Go value, but {} is an empty JSON value
   223  				"StructB":    onlyV1, // Struct{...} is NOT an empty Go value, but {} is an empty JSON value
   224  				"StructC":    true,
   225  				"SliceA":     false,
   226  				"SliceB":     false,
   227  				"SliceC":     true,
   228  				"Array":      true,
   229  				"PointerA":   false,
   230  				"PointerB":   onlyV1, // new(string) is NOT a nil Go pointer, but "" is an empty JSON value
   231  				"PointerC":   true,
   232  				"InterfaceA": false,
   233  				"InterfaceB": onlyV1, // (*string)(nil) is NOT a nil Go interface, but null is an empty JSON value
   234  				"InterfaceC": onlyV1, // new(string) is NOT a nil Go interface, but "" is an empty JSON value
   235  				"InterfaceD": true,
   236  			}
   237  			for field, want := range wantPresent {
   238  				_, got := out[field]
   239  				if got != want {
   240  					t.Fatalf("%T.%s = %v, want %v", in, field, got, want)
   241  				}
   242  			}
   243  		})
   244  	}
   245  }
   246  
   247  func addr[T any](v T) *T {
   248  	return &v
   249  }
   250  
   251  // In v1, the "string" option specifies that Go bools and numeric values are
   252  // encoded within a JSON string when marshaling and are unmarshaled from
   253  // either the native JSON representation (i.e., a JSON bool or number) or
   254  // its native representation escaped within a JSON string.
   255  // The "string" option is not applied recursively, and
   256  // so does not affect bools and numeric values within a Go slice or map, but
   257  // does have special handling to affect the underlying value within a pointer.
   258  // When unmarshaling, the "string" option permits decoding from a JSON null
   259  // escaped within a JSON string in some inconsistent cases.
   260  //
   261  // In v2, the "string" option specifies that only numeric values are encoded as
   262  // a JSON number within a JSON string when marshaling and are unmarshaled
   263  // from either a JSON number or a JSON string containing a JSON number.
   264  // The "string" option is applied recursively to all numeric sub-values,
   265  // and thus affects numeric values within a Go slice or map.
   266  // There is no support for escaped JSON nulls within a JSON string.
   267  //
   268  // The main utility for stringifying JSON primitives (i.e., bools and numbers)
   269  // is because JSON parsers often represents numbers as IEEE 754
   270  // floating-point numbers. This results in a loss of precision when trying to
   271  // represent 64-bit integer values. Consequently, many JSON-based APIs actually
   272  // requires that such values be encoded within a JSON string.
   273  // Given the main utility of stringification is for numeric values,
   274  // v2 limits the effect of the "string" option to just numeric Go types.
   275  // According to all code known by the Go module proxy,
   276  // there are close to zero usages of the "string" option with a Go bool.
   277  //
   278  // Regarding the recursive application of the "string" option,
   279  // there have been a number of issues filed about users being surprised that
   280  // the "string" option does not recursively affect numeric values
   281  // within a composite type like a Go map, slice, or interface value.
   282  // In v1, specifying the "string" option on composite type has no effect
   283  // and so this would be a largely backwards compatible change.
   284  //
   285  // The ability to decode from a JSON null wrapped within a JSON string
   286  // is removed in v2 because this behavior was surprising and inconsistent in v1.
   287  //
   288  // Related issues:
   289  //
   290  //	https://go.dev/issue/15624
   291  //	https://go.dev/issue/20651
   292  //	https://go.dev/issue/22177
   293  //	https://go.dev/issue/32055
   294  //	https://go.dev/issue/32117
   295  //	https://go.dev/issue/50997
   296  func TestStringOption(t *testing.T) {
   297  	type Types struct {
   298  		Bool       bool                `json:",string"`
   299  		Int        int                 `json:",string"`
   300  		Float      float64             `json:",string"`
   301  		Map        map[string]int      `json:",string"`
   302  		Struct     struct{ Field int } `json:",string"`
   303  		Slice      []int               `json:",string"`
   304  		Array      [1]int              `json:",string"`
   305  		PointerA   *int                `json:",string"`
   306  		PointerB   *int                `json:",string"`
   307  		PointerC   **int               `json:",string"`
   308  		InterfaceA any                 `json:",string"`
   309  		InterfaceB any                 `json:",string"`
   310  	}
   311  
   312  	for _, json := range jsonPackages {
   313  		t.Run(path.Join("Marshal", json.Version), func(t *testing.T) {
   314  			in := Types{
   315  				Bool:       true,
   316  				Int:        1,
   317  				Float:      1,
   318  				Map:        map[string]int{"Name": 1},
   319  				Struct:     struct{ Field int }{1},
   320  				Slice:      []int{1},
   321  				Array:      [1]int{1},
   322  				PointerA:   nil,
   323  				PointerB:   addr(1),
   324  				PointerC:   addr(addr(1)),
   325  				InterfaceA: nil,
   326  				InterfaceB: 1,
   327  			}
   328  			quote := func(s string) string { return `"` + s + `"` }
   329  			quoteOnlyV1 := func(s string) string {
   330  				if json.Version == "v1" {
   331  					s = quote(s)
   332  				}
   333  				return s
   334  			}
   335  			quoteOnlyV2 := func(s string) string {
   336  				if json.Version == "v2" {
   337  					s = quote(s)
   338  				}
   339  				return s
   340  			}
   341  			want := strings.Join([]string{
   342  				`{`,
   343  				`"Bool":` + quoteOnlyV1("true") + `,`, // in v1, Go bool are also stringified
   344  				`"Int":` + quote("1") + `,`,
   345  				`"Float":` + quote("1") + `,`,
   346  				`"Map":{"Name":` + quoteOnlyV2("1") + `},`,     // in v2, numbers are recursively stringified
   347  				`"Struct":{"Field":` + quoteOnlyV2("1") + `},`, // in v2, numbers are recursively stringified
   348  				`"Slice":[` + quoteOnlyV2("1") + `],`,          // in v2, numbers are recursively stringified
   349  				`"Array":[` + quoteOnlyV2("1") + `],`,          // in v2, numbers are recursively stringified
   350  				`"PointerA":null,`,
   351  				`"PointerB":` + quote("1") + `,`,       // in v1, numbers are stringified after a single pointer indirection
   352  				`"PointerC":` + quoteOnlyV2("1") + `,`, // in v2, numbers are recursively stringified
   353  				`"InterfaceA":null,`,
   354  				`"InterfaceB":` + quoteOnlyV2("1") + ``, // in v2, numbers are recursively stringified
   355  				`}`}, "")
   356  			got, err := json.Marshal(in)
   357  			if err != nil {
   358  				t.Fatalf("json.Marshal error: %v", err)
   359  			}
   360  			if string(got) != want {
   361  				t.Fatalf("json.Marshal = %s, want %s", got, want)
   362  			}
   363  		})
   364  	}
   365  
   366  	for _, json := range jsonPackages {
   367  		t.Run(path.Join("Unmarshal/Null", json.Version), func(t *testing.T) {
   368  			var got Types
   369  			err := json.Unmarshal([]byte(`{
   370  				"Bool":     "null",
   371  				"Int":      "null",
   372  				"PointerA": "null"
   373  			}`), &got)
   374  			switch {
   375  			case !reflect.DeepEqual(got, Types{}):
   376  				t.Fatalf("json.Unmarshal = %v, want %v", got, Types{})
   377  			case json.Version == "v1" && err != nil:
   378  				t.Fatalf("json.Unmarshal error: %v", err)
   379  			case json.Version == "v2" && err == nil:
   380  				t.Fatal("json.Unmarshal error is nil, want non-nil")
   381  			}
   382  		})
   383  
   384  		t.Run(path.Join("Unmarshal/Bool", json.Version), func(t *testing.T) {
   385  			var got Types
   386  			want := map[string]Types{
   387  				"v1": {Bool: true},
   388  				"v2": {Bool: false},
   389  			}[json.Version]
   390  			err := json.Unmarshal([]byte(`{"Bool": "true"}`), &got)
   391  			switch {
   392  			case !reflect.DeepEqual(got, want):
   393  				t.Fatalf("json.Unmarshal = %v, want %v", got, want)
   394  			case json.Version == "v1" && err != nil:
   395  				t.Fatalf("json.Unmarshal error: %v", err)
   396  			case json.Version == "v2" && err == nil:
   397  				t.Fatal("json.Unmarshal error is nil, want non-nil")
   398  			}
   399  		})
   400  
   401  		t.Run(path.Join("Unmarshal/Shallow", json.Version), func(t *testing.T) {
   402  			var got Types
   403  			want := Types{Int: 1, PointerB: addr(1)}
   404  			err := json.Unmarshal([]byte(`{
   405  				"Int":      "1",
   406  				"PointerB": "1"
   407  			}`), &got)
   408  			switch {
   409  			case !reflect.DeepEqual(got, want):
   410  				t.Fatalf("json.Unmarshal = %v, want %v", got, want)
   411  			case err != nil:
   412  				t.Fatalf("json.Unmarshal error: %v", err)
   413  			}
   414  		})
   415  
   416  		t.Run(path.Join("Unmarshal/Deep", json.Version), func(t *testing.T) {
   417  			var got Types
   418  			want := map[string]Types{
   419  				"v1": {
   420  					Map:      map[string]int{"Name": 0},
   421  					Slice:    []int{0},
   422  					PointerC: addr(addr(0)),
   423  				},
   424  				"v2": {
   425  					Map:      map[string]int{"Name": 1},
   426  					Struct:   struct{ Field int }{1},
   427  					Slice:    []int{1},
   428  					Array:    [1]int{1},
   429  					PointerC: addr(addr(1)),
   430  				},
   431  			}[json.Version]
   432  			err := json.Unmarshal([]byte(`{
   433  				"Map":      {"Name":"1"},
   434  				"Struct":   {"Field":"1"},
   435  				"Slice":    ["1"],
   436  				"Array":    ["1"],
   437  				"PointerC": "1"
   438  			}`), &got)
   439  			switch {
   440  			case !reflect.DeepEqual(got, want):
   441  				t.Fatalf("json.Unmarshal =\n%v, want\n%v", got, want)
   442  			case json.Version == "v1" && err == nil:
   443  				t.Fatal("json.Unmarshal error is nil, want non-nil")
   444  			case json.Version == "v2" && err != nil:
   445  				t.Fatalf("json.Unmarshal error: %v", err)
   446  			}
   447  		})
   448  	}
   449  }
   450  
   451  // In v1, nil slices and maps are marshaled as a JSON null.
   452  // In v2, nil slices and maps are marshaled as an empty JSON object or array.
   453  //
   454  // Users of v2 can opt into the v1 behavior by setting
   455  // the "format:emitnull" option in the `json` struct field tag:
   456  //
   457  //	struct {
   458  //		S []string          `json:",format:emitnull"`
   459  //		M map[string]string `json:",format:emitnull"`
   460  //	}
   461  //
   462  // JSON is a language-agnostic data interchange format.
   463  // The fact that maps and slices are nil-able in Go is a semantic detail of the
   464  // Go language. We should avoid leaking such details to the JSON representation.
   465  // When JSON implementations leak language-specific details,
   466  // it complicates transition to/from languages with different type systems.
   467  //
   468  // Furthermore, consider two related Go types: string and []byte.
   469  // It's an asymmetric oddity of v1 that zero values of string and []byte marshal
   470  // as an empty JSON string for the former, while the latter as a JSON null.
   471  // The non-zero values of those types always marshal as JSON strings.
   472  //
   473  // Related issues:
   474  //
   475  //	https://go.dev/issue/27589
   476  //	https://go.dev/issue/37711
   477  func TestNilSlicesAndMaps(t *testing.T) {
   478  	type Composites struct {
   479  		B []byte            // always encoded in v2 as a JSON string
   480  		S []string          // always encoded in v2 as a JSON array
   481  		M map[string]string // always encoded in v2 as a JSON object
   482  	}
   483  
   484  	for _, json := range jsonPackages {
   485  		t.Run(path.Join("Marshal", json.Version), func(t *testing.T) {
   486  			in := []Composites{
   487  				{B: []byte(nil), S: []string(nil), M: map[string]string(nil)},
   488  				{B: []byte{}, S: []string{}, M: map[string]string{}},
   489  			}
   490  			want := map[string]string{
   491  				"v1": `[{"B":null,"S":null,"M":null},{"B":"","S":[],"M":{}}]`,
   492  				"v2": `[{"B":"","S":[],"M":{}},{"B":"","S":[],"M":{}}]`, // v2 emits nil slices and maps as empty JSON objects and arrays
   493  			}[json.Version]
   494  			got, err := json.Marshal(in)
   495  			if err != nil {
   496  				t.Fatalf("json.Marshal error: %v", err)
   497  			}
   498  			if string(got) != want {
   499  				t.Fatalf("json.Marshal = %s, want %s", got, want)
   500  			}
   501  		})
   502  	}
   503  }
   504  
   505  // In v1, unmarshaling into a Go array permits JSON arrays with any length.
   506  // In v2, unmarshaling into a Go array requires that the JSON array
   507  // have the exact same number of elements as the Go array.
   508  //
   509  // Go arrays are often used because the exact length has significant meaning.
   510  // Ignoring this detail seems like a mistake. Also, the v1 behavior leads to
   511  // silent data loss when excess JSON array elements are discarded.
   512  func TestArrays(t *testing.T) {
   513  	for _, json := range jsonPackages {
   514  		t.Run(path.Join("Unmarshal/TooFew", json.Version), func(t *testing.T) {
   515  			var got [2]int
   516  			err := json.Unmarshal([]byte(`[1]`), &got)
   517  			switch {
   518  			case got != [2]int{1, 0}:
   519  				t.Fatalf(`json.Unmarshal = %v, want [1 0]`, got)
   520  			case json.Version == "v1" && err != nil:
   521  				t.Fatalf("json.Unmarshal error: %v", err)
   522  			case json.Version == "v2" && err == nil:
   523  				t.Fatal("json.Unmarshal error is nil, want non-nil")
   524  			}
   525  		})
   526  	}
   527  
   528  	for _, json := range jsonPackages {
   529  		t.Run(path.Join("Unmarshal/TooMany", json.Version), func(t *testing.T) {
   530  			var got [2]int
   531  			err := json.Unmarshal([]byte(`[1,2,3]`), &got)
   532  			switch {
   533  			case got != [2]int{1, 2}:
   534  				t.Fatalf(`json.Unmarshal = %v, want [1 2]`, got)
   535  			case json.Version == "v1" && err != nil:
   536  				t.Fatalf("json.Unmarshal error: %v", err)
   537  			case json.Version == "v2" && err == nil:
   538  				t.Fatal("json.Unmarshal error is nil, want non-nil")
   539  			}
   540  		})
   541  	}
   542  }
   543  
   544  // In v1, byte arrays are treated as arrays of unsigned integers.
   545  // In v2, byte arrays are treated as binary values (similar to []byte).
   546  // This is to make the behavior of [N]byte and []byte more consistent.
   547  //
   548  // Users of v2 can opt into the v1 behavior by setting
   549  // the "format:array" option in the `json` struct field tag:
   550  //
   551  //	struct {
   552  //		B [32]byte `json:",format:array"`
   553  //	}
   554  func TestByteArrays(t *testing.T) {
   555  	for _, json := range jsonPackages {
   556  		t.Run(path.Join("Marshal", json.Version), func(t *testing.T) {
   557  			in := [4]byte{1, 2, 3, 4}
   558  			got, err := json.Marshal(in)
   559  			if err != nil {
   560  				t.Fatalf("json.Marshal error: %v", err)
   561  			}
   562  			want := map[string]string{
   563  				"v1": `[1,2,3,4]`,
   564  				"v2": `"AQIDBA=="`,
   565  			}[json.Version]
   566  			if string(got) != want {
   567  				t.Fatalf("json.Marshal = %s, want %s", got, want)
   568  			}
   569  		})
   570  	}
   571  
   572  	for _, json := range jsonPackages {
   573  		t.Run(path.Join("Unmarshal", json.Version), func(t *testing.T) {
   574  			in := map[string]string{
   575  				"v1": `[1,2,3,4]`,
   576  				"v2": `"AQIDBA=="`,
   577  			}[json.Version]
   578  			var got [4]byte
   579  			err := json.Unmarshal([]byte(in), &got)
   580  			switch {
   581  			case err != nil:
   582  				t.Fatalf("json.Unmarshal error: %v", err)
   583  			case got != [4]byte{1, 2, 3, 4}:
   584  				t.Fatalf("json.Unmarshal = %v, want [1 2 3 4]", got)
   585  			}
   586  		})
   587  	}
   588  }
   589  
   590  // CallCheck implements json.{Marshaler,Unmarshaler} on a pointer receiver.
   591  type CallCheck string
   592  
   593  // MarshalJSON always returns a JSON string with the literal "CALLED".
   594  func (*CallCheck) MarshalJSON() ([]byte, error) {
   595  	return []byte(`"CALLED"`), nil
   596  }
   597  
   598  // UnmarshalJSON always stores a string with the literal "CALLED".
   599  func (v *CallCheck) UnmarshalJSON([]byte) error {
   600  	*v = `CALLED`
   601  	return nil
   602  }
   603  
   604  // In v1, the implementation is inconsistent about whether it calls
   605  // MarshalJSON and UnmarshalJSON methods declared on pointer receivers
   606  // when it has an unaddressable value (per reflect.Value.CanAddr) on hand.
   607  // When marshaling, it never boxes the value on the heap to make it addressable,
   608  // while it sometimes boxes values (e.g., for map entries) when unmarshaling.
   609  //
   610  // In v2, the implementation always calls MarshalJSON and UnmarshalJSON methods
   611  // by boxing the value on the heap if necessary.
   612  //
   613  // The v1 behavior is surprising at best and buggy at worst.
   614  // Unfortunately, it cannot be changed without breaking existing usages.
   615  //
   616  // Related issues:
   617  //
   618  //	https://go.dev/issue/27722
   619  //	https://go.dev/issue/33993
   620  //	https://go.dev/issue/42508
   621  func TestPointerReceiver(t *testing.T) {
   622  	type Values struct {
   623  		S []CallCheck
   624  		A [1]CallCheck
   625  		M map[string]CallCheck
   626  		V CallCheck
   627  		I any
   628  	}
   629  
   630  	for _, json := range jsonPackages {
   631  		t.Run(path.Join("Marshal", json.Version), func(t *testing.T) {
   632  			var cc CallCheck
   633  			in := Values{
   634  				S: []CallCheck{cc},
   635  				A: [1]CallCheck{cc},             // MarshalJSON not called on v1
   636  				M: map[string]CallCheck{"": cc}, // MarshalJSON not called on v1
   637  				V: cc,                           // MarshalJSON not called on v1
   638  				I: cc,                           // MarshalJSON not called on v1
   639  			}
   640  			want := map[string]string{
   641  				"v1": `{"S":["CALLED"],"A":[""],"M":{"":""},"V":"","I":""}`,
   642  				"v2": `{"S":["CALLED"],"A":["CALLED"],"M":{"":"CALLED"},"V":"CALLED","I":"CALLED"}`,
   643  			}[json.Version]
   644  			got, err := json.Marshal(in)
   645  			if err != nil {
   646  				t.Fatalf("json.Marshal error: %v", err)
   647  			}
   648  			if string(got) != want {
   649  				t.Fatalf("json.Marshal = %s, want %s", got, want)
   650  			}
   651  		})
   652  	}
   653  
   654  	for _, json := range jsonPackages {
   655  		t.Run(path.Join("Unmarshal", json.Version), func(t *testing.T) {
   656  			in := `{"S":[""],"A":[""],"M":{"":""},"V":"","I":""}`
   657  			called := CallCheck("CALLED") // resulting state if UnmarshalJSON is called
   658  			want := map[string]Values{
   659  				"v1": {
   660  					S: []CallCheck{called},
   661  					A: [1]CallCheck{called},
   662  					M: map[string]CallCheck{"": called},
   663  					V: called,
   664  					I: "", // UnmarshalJSON not called on v1; replaced with Go string
   665  				},
   666  				"v2": {
   667  					S: []CallCheck{called},
   668  					A: [1]CallCheck{called},
   669  					M: map[string]CallCheck{"": called},
   670  					V: called,
   671  					I: called,
   672  				},
   673  			}[json.Version]
   674  			got := Values{
   675  				A: [1]CallCheck{CallCheck("")},
   676  				S: []CallCheck{CallCheck("")},
   677  				M: map[string]CallCheck{"": CallCheck("")},
   678  				V: CallCheck(""),
   679  				I: CallCheck(""),
   680  			}
   681  			if err := json.Unmarshal([]byte(in), &got); err != nil {
   682  				t.Fatalf("json.Unmarshal error: %v", err)
   683  			}
   684  			if !reflect.DeepEqual(got, want) {
   685  				t.Fatalf("json.Unmarshal = %v, want %v", got, want)
   686  			}
   687  		})
   688  	}
   689  }
   690  
   691  // In v1, maps are marshaled in a deterministic order.
   692  // In v2, maps are marshaled in a non-deterministic order.
   693  //
   694  // The reason for the change is that v2 prioritizes performance and
   695  // the guarantee that marshaling operates primarily in a streaming manner.
   696  //
   697  // The v2 API provides RawValue.Canonicalize if stability is needed:
   698  //
   699  //	(*json.RawValue)(&b).Canonicalize()
   700  //
   701  // Related issue:
   702  //
   703  //	https://go.dev/issue/7872
   704  //	https://go.dev/issue/33714
   705  func TestMapDeterminism(t *testing.T) {
   706  	const iterations = 10
   707  	in := map[int]int{0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9}
   708  
   709  	for _, json := range jsonPackages {
   710  		t.Run(path.Join("Marshal", json.Version), func(t *testing.T) {
   711  			outs := make(map[string]bool)
   712  			for i := 0; i < iterations; i++ {
   713  				b, err := json.Marshal(in)
   714  				if err != nil {
   715  					t.Fatalf("json.Marshal error: %v", err)
   716  				}
   717  				outs[string(b)] = true
   718  			}
   719  			switch {
   720  			case json.Version == "v1" && len(outs) != 1:
   721  				t.Fatalf("json.Marshal encoded to %d unique forms, expected 1", len(outs))
   722  			case json.Version == "v2" && len(outs) == 1:
   723  				t.Logf("json.Marshal encoded to 1 unique form by chance; are you feeling lucky?")
   724  			}
   725  		})
   726  	}
   727  }
   728  
   729  // In v1, JSON string encoding escapes special characters related to HTML.
   730  // In v2, JSON string encoding uses a normalized representation (per RFC 8785).
   731  //
   732  // Users of v2 can opt into the v1 behavior by setting
   733  // json.EncodeOptions.EscapeRune. See the EscapeHTML example.
   734  //
   735  // Escaping HTML-specific characters in a JSON library is a layering violation.
   736  // It presumes that JSON is always used with HTML and ignores other
   737  // similar classes of injection attacks (e.g., SQL injection).
   738  // Users of JSON with HTML should either manually ensure that embedded JSON is
   739  // properly escaped or be relying on a module like "github.com/google/safehtml"
   740  // to handle safe interoperability of JSON and HTML.
   741  func TestEscapeHTML(t *testing.T) {
   742  	for _, json := range jsonPackages {
   743  		t.Run(path.Join("Marshal", json.Version), func(t *testing.T) {
   744  			const in = `<script> console.log("Hello, world!"); </script>`
   745  			got, err := json.Marshal(in)
   746  			if err != nil {
   747  				t.Fatalf("json.Marshal error: %v", err)
   748  			}
   749  			want := map[string]string{
   750  				"v1": `"\u003cscript\u003e console.log(\"Hello, world!\"); \u003c/script\u003e"`,
   751  				"v2": `"<script> console.log(\"Hello, world!\"); </script>"`,
   752  			}[json.Version]
   753  			if string(got) != want {
   754  				t.Fatalf("json.Marshal = %s, want %s", got, want)
   755  			}
   756  		})
   757  	}
   758  }
   759  
   760  // In v1, JSON serialization silently ignored invalid UTF-8 by
   761  // replacing such bytes with the Unicode replacement character.
   762  // In v2, JSON serialization reports an error if invalid UTF-8 is encountered.
   763  //
   764  // Users of v2 can opt into the v1 behavior by setting
   765  // AllowInvalidUTF8 to true in json.EncodeOptions or json.DecodeOptions:
   766  //
   767  //	json.MarshalOptions{...}.Marshal(json.EncodeOptions{AllowInvalidUTF8: true}, ...)
   768  //	json.UnmarshalOptions{...}.Unmarshal(json.DecodeOptions{AllowInvalidUTF8: true}, ...)
   769  //
   770  // Silently allowing invalid UTF-8 causes data corruption that can be difficult
   771  // to detect until it is too late. Once it has been discovered, strict UTF-8
   772  // behavior sometimes cannot be enabled since other logic may be depending
   773  // on the current behavior due to Hyrum's Law.
   774  //
   775  // Tim Bray, the author of RFC 8259 recommends that implementations should
   776  // go beyond RFC 8259 and instead target compliance with RFC 7493,
   777  // which makes strict decisions about behavior left undefined in RFC 8259.
   778  // In particular, RFC 7493 rejects the presence of invalid UTF-8.
   779  // See https://www.tbray.org/ongoing/When/201x/2017/12/14/RFC-8259-STD-90
   780  func TestInvalidUTF8(t *testing.T) {
   781  	for _, json := range jsonPackages {
   782  		t.Run(path.Join("Marshal", json.Version), func(t *testing.T) {
   783  			got, err := json.Marshal("\xff")
   784  			switch {
   785  			case json.Version == "v1" && err != nil:
   786  				t.Fatalf("json.Marshal error: %v", err)
   787  			case json.Version == "v1" && string(got) != `"\ufffd"`:
   788  				t.Fatalf(`json.Marshal = %s, want "\ufffd"`, got)
   789  			case json.Version == "v2" && err == nil:
   790  				t.Fatal("json.Marshal error is nil, want non-nil")
   791  			}
   792  		})
   793  	}
   794  
   795  	for _, json := range jsonPackages {
   796  		t.Run(path.Join("Unmarshal", json.Version), func(t *testing.T) {
   797  			const in = "\"\xff\""
   798  			var got string
   799  			err := json.Unmarshal([]byte(in), &got)
   800  			switch {
   801  			case json.Version == "v1" && err != nil:
   802  				t.Fatalf("json.Unmarshal error: %v", err)
   803  			case json.Version == "v1" && got != "\ufffd":
   804  				t.Fatalf(`json.Unmarshal = %q, want "\ufffd"`, got)
   805  			case json.Version == "v2" && err == nil:
   806  				t.Fatal("json.Unmarshal error is nil, want non-nil")
   807  			}
   808  		})
   809  	}
   810  }
   811  
   812  // In v1, duplicate JSON object names are permitted by default where
   813  // they follow the inconsistent and difficult-to-explain merge semantics of v1.
   814  // In v2, duplicate JSON object names are rejected by default where
   815  // they follow the merge semantics of v2 based on RFC 7396.
   816  //
   817  // Users of v2 can opt into the v1 behavior by setting
   818  // AllowDuplicateNames to true in json.EncodeOptions or json.DecodeOptions:
   819  //
   820  //	json.MarshalOptions{...}.Marshal(json.EncodeOptions{AllowDuplicateNames: true}, ...)
   821  //	json.UnmarshalOptions{...}.Unmarshal(json.DecodeOptions{AllowDuplicateNames: true}, ...)
   822  //
   823  // Per RFC 8259, the handling of duplicate names is left as undefined behavior.
   824  // Rejecting such inputs is within the realm of valid behavior.
   825  // Tim Bray, the author of RFC 8259 recommends that implementations should
   826  // go beyond RFC 8259 and instead target compliance with RFC 7493,
   827  // which makes strict decisions about behavior left undefined in RFC 8259.
   828  // In particular, RFC 7493 rejects the presence of duplicate object names.
   829  // See https://www.tbray.org/ongoing/When/201x/2017/12/14/RFC-8259-STD-90
   830  //
   831  // The lack of duplicate name rejection has correctness implications where
   832  // roundtrip unmarshal/marshal do not result in semantically equivalent JSON.
   833  // This is surprising behavior for users when they accidentally
   834  // send JSON objects with duplicate names.
   835  //
   836  // The lack of duplicate name rejection may have security implications since it
   837  // becomes difficult for a security tool to validate the semantic meaning of a
   838  // JSON object since meaning is undefined in the presence of duplicate names.
   839  // See https://labs.bishopfox.com/tech-blog/an-exploration-of-json-interoperability-vulnerabilities
   840  //
   841  // Related issue:
   842  //
   843  //	https://go.dev/issue/48298
   844  func TestDuplicateNames(t *testing.T) {
   845  	for _, json := range jsonPackages {
   846  		t.Run(path.Join("Unmarshal", json.Version), func(t *testing.T) {
   847  			const in = `{"Name":1,"Name":2}`
   848  			var got struct{ Name int }
   849  			err := json.Unmarshal([]byte(in), &got)
   850  			switch {
   851  			case json.Version == "v1" && err != nil:
   852  				t.Fatalf("json.Unmarshal error: %v", err)
   853  			case json.Version == "v1" && got != struct{ Name int }{2}:
   854  				t.Fatalf(`json.Unmarshal = %v, want {2}`, got)
   855  			case json.Version == "v2" && err == nil:
   856  				t.Fatal("json.Unmarshal error is nil, want non-nil")
   857  			}
   858  		})
   859  	}
   860  }
   861  
   862  // In v1, unmarshaling a JSON null into a non-empty value was inconsistent
   863  // in that sometimes it would be ignored and other times clear the value.
   864  // In v2, unmarshaling a JSON null into a non-empty value would consistently
   865  // always clear the value regardless of the value's type.
   866  //
   867  // The purpose of this change is to have consistent behavior with how JSON nulls
   868  // are handled during Unmarshal. This semantic detail has no effect
   869  // when Unmarshaling into a empty value.
   870  //
   871  // Related issues:
   872  //
   873  //	https://go.dev/issue/22177
   874  //	https://go.dev/issue/33835
   875  func TestMergeNull(t *testing.T) {
   876  	type Types struct {
   877  		Bool      bool
   878  		String    string
   879  		Bytes     []byte
   880  		Int       int
   881  		Map       map[string]string
   882  		Struct    struct{ Field string }
   883  		Slice     []string
   884  		Array     [1]string
   885  		Pointer   *string
   886  		Interface any
   887  	}
   888  
   889  	for _, json := range jsonPackages {
   890  		t.Run(path.Join("Unmarshal", json.Version), func(t *testing.T) {
   891  			// Start with a non-empty value where all fields are populated.
   892  			in := Types{
   893  				Bool:      true,
   894  				String:    "old",
   895  				Bytes:     []byte("old"),
   896  				Int:       1234,
   897  				Map:       map[string]string{"old": "old"},
   898  				Struct:    struct{ Field string }{"old"},
   899  				Slice:     []string{"old"},
   900  				Array:     [1]string{"old"},
   901  				Pointer:   new(string),
   902  				Interface: "old",
   903  			}
   904  
   905  			// Unmarshal a JSON null into every field.
   906  			if err := json.Unmarshal([]byte(`{
   907  				"Bool":      null,
   908  				"String":    null,
   909  				"Bytes":     null,
   910  				"Int":       null,
   911  				"Map":       null,
   912  				"Struct":    null,
   913  				"Slice":     null,
   914  				"Array":     null,
   915  				"Pointer":   null,
   916  				"Interface": null
   917  			}`), &in); err != nil {
   918  				t.Fatalf("json.Unmarshal error: %v", err)
   919  			}
   920  
   921  			want := map[string]Types{
   922  				"v1": {
   923  					Bool:   true,
   924  					String: "old",
   925  					Int:    1234,
   926  					Struct: struct{ Field string }{"old"},
   927  					Array:  [1]string{"old"},
   928  				},
   929  				"v2": {}, // all fields are zeroed
   930  			}[json.Version]
   931  			if !reflect.DeepEqual(in, want) {
   932  				t.Fatalf("json.Unmarshal = %+v, want %+v", in, want)
   933  			}
   934  		})
   935  	}
   936  }
   937  
   938  // In v1, merge semantics are inconsistent and difficult to explain.
   939  // In v2, merge semantics replaces the destination value for anything
   940  // other than a JSON object, and recursively merges JSON objects.
   941  //
   942  // Merge semantics in v1 are inconsistent and difficult to explain
   943  // largely because the behavior came about organically, rather than
   944  // having a principled approach to how the semantics should operate.
   945  // In v2, merging follows behavior based on RFC 7396.
   946  //
   947  // Related issues:
   948  //
   949  //	https://go.dev/issue/21092
   950  //	https://go.dev/issue/26946
   951  //	https://go.dev/issue/27172
   952  //	https://go.dev/issue/30701
   953  //	https://go.dev/issue/31924
   954  //	https://go.dev/issue/43664
   955  func TestMergeComposite(t *testing.T) {
   956  	type Tuple struct{ Old, New bool }
   957  	type Composites struct {
   958  		Slice            []Tuple
   959  		Array            [1]Tuple
   960  		Map              map[string]Tuple
   961  		MapPointer       map[string]*Tuple
   962  		Struct           struct{ Tuple Tuple }
   963  		StructPointer    *struct{ Tuple Tuple }
   964  		Interface        any
   965  		InterfacePointer any
   966  	}
   967  
   968  	for _, json := range jsonPackages {
   969  		t.Run(path.Join("Unmarshal", json.Version), func(t *testing.T) {
   970  			// Start with a non-empty value where all fields are populated.
   971  			in := Composites{
   972  				Slice:            []Tuple{{Old: true}, {Old: true}}[:1],
   973  				Array:            [1]Tuple{{Old: true}},
   974  				Map:              map[string]Tuple{"Tuple": {Old: true}},
   975  				MapPointer:       map[string]*Tuple{"Tuple": {Old: true}},
   976  				Struct:           struct{ Tuple Tuple }{Tuple{Old: true}},
   977  				StructPointer:    &struct{ Tuple Tuple }{Tuple{Old: true}},
   978  				Interface:        Tuple{Old: true},
   979  				InterfacePointer: &Tuple{Old: true},
   980  			}
   981  
   982  			// Unmarshal into every pre-populated field.
   983  			if err := json.Unmarshal([]byte(`{
   984  				"Slice":            [{"New":true}, {"New":true}],
   985  				"Array":            [{"New":true}],
   986  				"Map":              {"Tuple": {"New":true}},
   987  				"MapPointer":       {"Tuple": {"New":true}},
   988  				"Struct":           {"Tuple": {"New":true}},
   989  				"StructPointer":    {"Tuple": {"New":true}},
   990  				"Interface":        {"New":true},
   991  				"InterfacePointer": {"New":true}
   992  			}`), &in); err != nil {
   993  				t.Fatalf("json.Unmarshal error: %v", err)
   994  			}
   995  
   996  			merged := Tuple{Old: true, New: true}
   997  			replaced := Tuple{Old: false, New: true}
   998  			want := map[string]Composites{
   999  				"v1": {
  1000  					Slice:            []Tuple{merged, merged},               // merged
  1001  					Array:            [1]Tuple{merged},                      // merged
  1002  					Map:              map[string]Tuple{"Tuple": replaced},   // replaced
  1003  					MapPointer:       map[string]*Tuple{"Tuple": &replaced}, // replaced
  1004  					Struct:           struct{ Tuple Tuple }{merged},         // merged (same as v2)
  1005  					StructPointer:    &struct{ Tuple Tuple }{merged},        // merged (same as v2)
  1006  					Interface:        map[string]any{"New": true},           // replaced
  1007  					InterfacePointer: &merged,                               // merged (same as v2)
  1008  				},
  1009  				"v2": {
  1010  					Slice:            []Tuple{replaced, replaced},         // replaced
  1011  					Array:            [1]Tuple{replaced},                  // replaced
  1012  					Map:              map[string]Tuple{"Tuple": merged},   // merged
  1013  					MapPointer:       map[string]*Tuple{"Tuple": &merged}, // merged
  1014  					Struct:           struct{ Tuple Tuple }{merged},       // merged (same as v1)
  1015  					StructPointer:    &struct{ Tuple Tuple }{merged},      // merged (same as v1)
  1016  					Interface:        merged,                              // merged
  1017  					InterfacePointer: &merged,                             // merged (same as v1)
  1018  				},
  1019  			}[json.Version]
  1020  			if !reflect.DeepEqual(in, want) {
  1021  				t.Fatalf("json.Unmarshal = %+v, want %+v", in, want)
  1022  			}
  1023  		})
  1024  	}
  1025  }
  1026  
  1027  // In v1, there was no special support for time.Duration,
  1028  // which resulted in that type simply being treated as a signed integer.
  1029  // In v2, there is now first-class support for time.Duration, where the type is
  1030  // formatted and parsed using time.Duration.String and time.ParseDuration.
  1031  //
  1032  // Users of v2 can opt into the v1 behavior by setting
  1033  // the "format:nanos" option in the `json` struct field tag:
  1034  //
  1035  //	struct {
  1036  //		Duration time.Duration `json:",format:nanos"`
  1037  //	}
  1038  //
  1039  // Related issue:
  1040  //
  1041  //	https://go.dev/issue/10275
  1042  func TestTimeDurations(t *testing.T) {
  1043  	for _, json := range jsonPackages {
  1044  		t.Run(path.Join("Marshal", json.Version), func(t *testing.T) {
  1045  			got, err := json.Marshal(time.Minute)
  1046  			switch {
  1047  			case err != nil:
  1048  				t.Fatalf("json.Marshal error: %v", err)
  1049  			case json.Version == "v1" && string(got) != "60000000000":
  1050  				t.Fatalf("json.Marshal = %s, want 60000000000", got)
  1051  			case json.Version == "v2" && string(got) != `"1m0s"`:
  1052  				t.Fatalf(`json.Marshal = %s, want "1m0s"`, got)
  1053  			}
  1054  		})
  1055  	}
  1056  
  1057  	for _, json := range jsonPackages {
  1058  		t.Run(path.Join("Unmarshal", json.Version), func(t *testing.T) {
  1059  			in := map[string]string{
  1060  				"v1": "60000000000",
  1061  				"v2": `"1m0s"`,
  1062  			}[json.Version]
  1063  			var got time.Duration
  1064  			err := json.Unmarshal([]byte(in), &got)
  1065  			switch {
  1066  			case err != nil:
  1067  				t.Fatalf("json.Unmarshal error: %v", err)
  1068  			case got != time.Minute:
  1069  				t.Fatalf("json.Unmarshal = %v, want 1m0s", got)
  1070  			}
  1071  		})
  1072  	}
  1073  }
  1074  
  1075  // In v1, unmarshaling a JSON number beyond the representation of a Go float
  1076  // would result in an error.
  1077  // In v2, unmarshaling a JSON number beyond the representation of a Go float
  1078  // would use the closest representable value (i.e., ±math.MaxFloatX).
  1079  //
  1080  // The rationale for the change is to ensure that
  1081  // if a JSON value is syntactically valid according to json.RawValue.IsValid,
  1082  // then it is always valid to unmarshal that into a Go any value.
  1083  func TestMaxFloats(t *testing.T) {
  1084  	for _, json := range jsonPackages {
  1085  		t.Run(path.Join("Unmarshal", json.Version), func(t *testing.T) {
  1086  			const in = `1e1000`
  1087  			var got any
  1088  			err := json.Unmarshal([]byte(in), &got)
  1089  			switch {
  1090  			case json.Version == "v1" && err == nil:
  1091  				t.Fatal("json.Unmarshal error is nil, want non-nil")
  1092  			case json.Version == "v2" && got != any(math.MaxFloat64):
  1093  				t.Fatalf("json.Unmarshal = %v, want %v", got, math.MaxFloat64)
  1094  			case json.Version == "v2" && err != nil:
  1095  				t.Fatalf("json.Unmarshal error: %v", err)
  1096  			}
  1097  		})
  1098  	}
  1099  }
  1100  
  1101  // In v1, non-empty structs without any JSON serializable fields are permitted.
  1102  // In v2, non-empty structs without any JSON serializable fields are rejected.
  1103  //
  1104  // The purpose of this change is to avoid a common pitfall for new users
  1105  // where they expect JSON serialization to handle unexported fields.
  1106  // However, this does not work since Go reflection does not
  1107  // provide the package the ability to mutate such fields.
  1108  // Rejecting unserializable structs in v2 is intended to be a clear signal
  1109  // that the type is not supposed to be serialized.
  1110  func TestEmptyStructs(t *testing.T) {
  1111  	never := func(string) bool { return false }
  1112  	onlyV2 := func(v string) bool { return v == "v2" }
  1113  	values := []struct {
  1114  		in        any
  1115  		wantError func(string) bool
  1116  	}{
  1117  		// It is okay to marshal a truly empty struct in v1 and v2.
  1118  		{in: addr(struct{}{}), wantError: never},
  1119  		// In v1, a non-empty struct without exported fields
  1120  		// is equivalent to an empty struct, but is rejected in v2.
  1121  		// Note that errors.errorString type has only unexported fields.
  1122  		{in: errors.New("error"), wantError: onlyV2},
  1123  		// A mix of exported and unexported fields is permitted.
  1124  		{in: addr(struct{ Exported, unexported int }{}), wantError: never},
  1125  	}
  1126  
  1127  	for _, json := range jsonPackages {
  1128  		t.Run("Marshal", func(t *testing.T) {
  1129  			for _, value := range values {
  1130  				wantError := value.wantError(json.Version)
  1131  				_, err := json.Marshal(value.in)
  1132  				switch {
  1133  				case (err == nil) && wantError:
  1134  					t.Fatalf("json.Marshal error is nil, want non-nil")
  1135  				case (err != nil) && !wantError:
  1136  					t.Fatalf("json.Marshal error: %v", err)
  1137  				}
  1138  			}
  1139  		})
  1140  	}
  1141  
  1142  	for _, json := range jsonPackages {
  1143  		t.Run("Unmarshal", func(t *testing.T) {
  1144  			for _, value := range values {
  1145  				wantError := value.wantError(json.Version)
  1146  				out := reflect.New(reflect.TypeOf(value.in).Elem()).Interface()
  1147  				err := json.Unmarshal([]byte("{}"), out)
  1148  				switch {
  1149  				case (err == nil) && wantError:
  1150  					t.Fatalf("json.Unmarshal error is nil, want non-nil")
  1151  				case (err != nil) && !wantError:
  1152  					t.Fatalf("json.Unmarshal error: %v", err)
  1153  				}
  1154  			}
  1155  		})
  1156  	}
  1157  }
  1158  
  1159  // In v1, embedding an unexported type with exported fields is permitted.
  1160  // If it is an embedded pointer, then the struct cannot be unmarshaled into.
  1161  // In v2, embedding an unexported type with exported fields is never permitted.
  1162  //
  1163  // The visibility of exported fields promoted through an embedded unexported
  1164  // struct type is difficult to explain. Even worse, the use of Go reflection
  1165  // does not directly correspond to what the Go language permits or rejects.
  1166  //
  1167  // The ability to marshal/unmarshal exported fields of unexported embedded types
  1168  // came about organically and has been the subject of various bugs in v1.
  1169  // Currently, the implementation in v1 performs a few hacks to make this work
  1170  // partially since the Go reflect package does not directly permit access.
  1171  // This behavior is only partial since it is impossible to unmarshal into
  1172  // the promoted fields of an embedded pointer to an unexported struct type
  1173  // since the implementation cannot allocate the containing struct value
  1174  // because the embedded field is unexported.
  1175  //
  1176  // Due to all of the subtle behavior and nuances surrounding embedded
  1177  // unexported struct types, v2 rejects all such cases and requires that authors
  1178  // explicitly mark such fields as being ignored with `json:"-"`.
  1179  //
  1180  // Related issues:
  1181  //
  1182  //	https://go.dev/issue/21353
  1183  //	https://go.dev/issue/21357
  1184  //	https://go.dev/issue/24153
  1185  func TestEmbedUnexported(t *testing.T) {
  1186  	never := func(string) bool { return false }
  1187  	onlyV2 := func(v string) bool { return v == "v2" }
  1188  	always := func(string) bool { return true }
  1189  	type Exported struct{ Field string }
  1190  	type unexported struct{ Field string }
  1191  	values := []struct {
  1192  		in                 any
  1193  		wantMarshalError   func(string) bool
  1194  		wantUnmarshalError func(string) bool
  1195  	}{
  1196  		// Embedding exported types is permitted in both v1 and v2.
  1197  		{in: struct{ Exported }{}, wantMarshalError: never, wantUnmarshalError: never},
  1198  		{in: struct{ *Exported }{}, wantMarshalError: never, wantUnmarshalError: never},
  1199  		// In v2, embedded unexported types are always rejected (unless ignored).
  1200  		{in: struct{ unexported }{}, wantMarshalError: onlyV2, wantUnmarshalError: onlyV2},
  1201  		{in: struct{ *unexported }{}, wantMarshalError: onlyV2, wantUnmarshalError: always},
  1202  		// In v2, embedded unexported types must be explicitly ignored.
  1203  		{in: struct {
  1204  			unexported `json:"-"`
  1205  		}{}, wantMarshalError: never, wantUnmarshalError: never},
  1206  		{in: struct {
  1207  			*unexported `json:"-"`
  1208  		}{}, wantMarshalError: never, wantUnmarshalError: never},
  1209  	}
  1210  
  1211  	for _, json := range jsonPackages {
  1212  		t.Run("Marshal", func(t *testing.T) {
  1213  			for _, value := range values {
  1214  				wantError := value.wantMarshalError(json.Version)
  1215  				_, err := json.Marshal(value.in)
  1216  				switch {
  1217  				case (err == nil) && wantError:
  1218  					t.Fatalf("json.Marshal error is nil, want non-nil")
  1219  				case (err != nil) && !wantError:
  1220  					t.Fatalf("json.Marshal error: %v", err)
  1221  				}
  1222  			}
  1223  		})
  1224  	}
  1225  
  1226  	for _, json := range jsonPackages {
  1227  		t.Run("Unmarshal", func(t *testing.T) {
  1228  			for _, value := range values {
  1229  				wantError := value.wantUnmarshalError(json.Version)
  1230  				out := reflect.New(reflect.TypeOf(value.in)).Interface()
  1231  				err := json.Unmarshal([]byte(`{"Field":"Value"}`), out)
  1232  				switch {
  1233  				case (err == nil) && wantError:
  1234  					t.Fatalf("json.Unmarshal error is nil, want non-nil")
  1235  				case (err != nil) && !wantError:
  1236  					t.Fatalf("json.Unmarshal error: %v", err)
  1237  				}
  1238  			}
  1239  		})
  1240  	}
  1241  }