Source file src/encoding/json/v2/example_test.go
1 // Copyright 2022 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 //go:build goexperiment.jsonv2 6 7 package json_test 8 9 import ( 10 "bytes" 11 "errors" 12 "fmt" 13 "io" 14 "log" 15 "net/http" 16 "net/netip" 17 "os" 18 "reflect" 19 "strconv" 20 "strings" 21 "sync/atomic" 22 "time" 23 24 "encoding/json/jsontext" 25 "encoding/json/v2" 26 ) 27 28 // If a type implements [encoding.TextMarshaler] and/or [encoding.TextUnmarshaler], 29 // then the MarshalText and UnmarshalText methods are used to encode/decode 30 // the value to/from a JSON string. 31 func Example_textMarshal() { 32 // Round-trip marshal and unmarshal a hostname map where the netip.Addr type 33 // implements both encoding.TextMarshaler and encoding.TextUnmarshaler. 34 want := map[netip.Addr]string{ 35 netip.MustParseAddr("192.168.0.100"): "carbonite", 36 netip.MustParseAddr("192.168.0.101"): "obsidian", 37 netip.MustParseAddr("192.168.0.102"): "diamond", 38 } 39 b, err := json.Marshal(&want, json.Deterministic(true)) 40 if err != nil { 41 log.Fatal(err) 42 } 43 var got map[netip.Addr]string 44 err = json.Unmarshal(b, &got) 45 if err != nil { 46 log.Fatal(err) 47 } 48 49 // Sanity check. 50 if !reflect.DeepEqual(got, want) { 51 log.Fatalf("roundtrip mismatch: got %v, want %v", got, want) 52 } 53 54 // Indent output for readability. 55 v := jsontext.Value(b) 56 v.Indent() 57 fmt.Println(string(v)) 58 59 // Output: 60 // { 61 // "192.168.0.100": "carbonite", 62 // "192.168.0.101": "obsidian", 63 // "192.168.0.102": "diamond" 64 // } 65 } 66 67 // By default, JSON object names for Go struct fields are derived from 68 // the Go field name, but may be specified in the `json` tag. 69 // Due to JSON's heritage in JavaScript, the most common naming convention 70 // used for JSON object names is camelCase. 71 func Example_fieldNames() { 72 var value struct { 73 // This field is explicitly ignored with the special "-" name. 74 Ignored any `json:"-"` 75 // No JSON name is not provided, so the Go field name is used. 76 GoName any 77 // A JSON name is provided without any special characters. 78 JSONName any `json:"jsonName"` 79 // No JSON name is not provided, so the Go field name is used. 80 Option any `json:",case:ignore"` 81 // An unexported field is always ignored. 82 unexported any 83 } 84 85 b, err := json.Marshal(value) 86 if err != nil { 87 log.Fatal(err) 88 } 89 90 // Indent output for readability. 91 v := jsontext.Value(b) 92 v.Indent() 93 fmt.Println(string(v)) 94 95 // Output: 96 // { 97 // "GoName": null, 98 // "jsonName": null, 99 // "Option": null 100 // } 101 } 102 103 // Unmarshal matches JSON object names with Go struct fields using 104 // a case-sensitive match, but can be configured to use a case-insensitive 105 // match with the "case:ignore" option. This permits unmarshaling from inputs 106 // that use naming conventions such as camelCase, snake_case, or kebab-case. 107 func Example_caseSensitivity() { 108 // JSON input using various naming conventions. 109 const input = `[ 110 {"firstname": true}, 111 {"firstName": true}, 112 {"FirstName": true}, 113 {"FIRSTNAME": true}, 114 {"first_name": true}, 115 {"FIRST_NAME": true}, 116 {"first-name": true}, 117 {"FIRST-NAME": true}, 118 {"unknown": true} 119 ]` 120 121 // Without "case:ignore", Unmarshal looks for an exact match. 122 var caseStrict []struct { 123 X bool `json:"firstName"` 124 } 125 if err := json.Unmarshal([]byte(input), &caseStrict); err != nil { 126 log.Fatal(err) 127 } 128 fmt.Println(caseStrict) // exactly 1 match found 129 130 // With "case:ignore", Unmarshal looks first for an exact match, 131 // then for a case-insensitive match if none found. 132 var caseIgnore []struct { 133 X bool `json:"firstName,case:ignore"` 134 } 135 if err := json.Unmarshal([]byte(input), &caseIgnore); err != nil { 136 log.Fatal(err) 137 } 138 fmt.Println(caseIgnore) // 8 matches found 139 140 // Output: 141 // [{false} {true} {false} {false} {false} {false} {false} {false} {false}] 142 // [{true} {true} {true} {true} {true} {true} {true} {true} {false}] 143 } 144 145 // Go struct fields can be omitted from the output depending on either 146 // the input Go value or the output JSON encoding of the value. 147 // The "omitzero" option omits a field if it is the zero Go value or 148 // implements a "IsZero() bool" method that reports true. 149 // The "omitempty" option omits a field if it encodes as an empty JSON value, 150 // which we define as a JSON null or empty JSON string, object, or array. 151 // In many cases, the behavior of "omitzero" and "omitempty" are equivalent. 152 // If both provide the desired effect, then using "omitzero" is preferred. 153 func Example_omitFields() { 154 type MyStruct struct { 155 Foo string `json:",omitzero"` 156 Bar []int `json:",omitempty"` 157 // Both "omitzero" and "omitempty" can be specified together, 158 // in which case the field is omitted if either would take effect. 159 // This omits the Baz field either if it is a nil pointer or 160 // if it would have encoded as an empty JSON object. 161 Baz *MyStruct `json:",omitzero,omitempty"` 162 } 163 164 // Demonstrate behavior of "omitzero". 165 b, err := json.Marshal(struct { 166 Bool bool `json:",omitzero"` 167 Int int `json:",omitzero"` 168 String string `json:",omitzero"` 169 Time time.Time `json:",omitzero"` 170 Addr netip.Addr `json:",omitzero"` 171 Struct MyStruct `json:",omitzero"` 172 SliceNil []int `json:",omitzero"` 173 Slice []int `json:",omitzero"` 174 MapNil map[int]int `json:",omitzero"` 175 Map map[int]int `json:",omitzero"` 176 PointerNil *string `json:",omitzero"` 177 Pointer *string `json:",omitzero"` 178 InterfaceNil any `json:",omitzero"` 179 Interface any `json:",omitzero"` 180 }{ 181 // Bool is omitted since false is the zero value for a Go bool. 182 Bool: false, 183 // Int is omitted since 0 is the zero value for a Go int. 184 Int: 0, 185 // String is omitted since "" is the zero value for a Go string. 186 String: "", 187 // Time is omitted since time.Time.IsZero reports true. 188 Time: time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC), 189 // Addr is omitted since netip.Addr{} is the zero value for a Go struct. 190 Addr: netip.Addr{}, 191 // Struct is NOT omitted since it is not the zero value for a Go struct. 192 Struct: MyStruct{Bar: []int{}, Baz: new(MyStruct)}, 193 // SliceNil is omitted since nil is the zero value for a Go slice. 194 SliceNil: nil, 195 // Slice is NOT omitted since []int{} is not the zero value for a Go slice. 196 Slice: []int{}, 197 // MapNil is omitted since nil is the zero value for a Go map. 198 MapNil: nil, 199 // Map is NOT omitted since map[int]int{} is not the zero value for a Go map. 200 Map: map[int]int{}, 201 // PointerNil is omitted since nil is the zero value for a Go pointer. 202 PointerNil: nil, 203 // Pointer is NOT omitted since new(string) is not the zero value for a Go pointer. 204 Pointer: new(string), 205 // InterfaceNil is omitted since nil is the zero value for a Go interface. 206 InterfaceNil: nil, 207 // Interface is NOT omitted since (*string)(nil) is not the zero value for a Go interface. 208 Interface: (*string)(nil), 209 }) 210 if err != nil { 211 log.Fatal(err) 212 } 213 // Indent output for readability. 214 v := jsontext.Value(b) 215 v.Indent() 216 fmt.Println("OmitZero:", string(v)) // outputs "Struct", "Slice", "Map", "Pointer", and "Interface" 217 218 // Demonstrate behavior of "omitempty". 219 b, err = json.Marshal(struct { 220 Bool bool `json:",omitempty"` 221 Int int `json:",omitempty"` 222 String string `json:",omitempty"` 223 Time time.Time `json:",omitempty"` 224 Addr netip.Addr `json:",omitempty"` 225 Struct MyStruct `json:",omitempty"` 226 Slice []int `json:",omitempty"` 227 Map map[int]int `json:",omitempty"` 228 PointerNil *string `json:",omitempty"` 229 Pointer *string `json:",omitempty"` 230 InterfaceNil any `json:",omitempty"` 231 Interface any `json:",omitempty"` 232 }{ 233 // Bool is NOT omitted since false is not an empty JSON value. 234 Bool: false, 235 // Int is NOT omitted since 0 is not a empty JSON value. 236 Int: 0, 237 // String is omitted since "" is an empty JSON string. 238 String: "", 239 // Time is NOT omitted since this encodes as a non-empty JSON string. 240 Time: time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC), 241 // Addr is omitted since this encodes as an empty JSON string. 242 Addr: netip.Addr{}, 243 // Struct is omitted since {} is an empty JSON object. 244 Struct: MyStruct{Bar: []int{}, Baz: new(MyStruct)}, 245 // Slice is omitted since [] is an empty JSON array. 246 Slice: []int{}, 247 // Map is omitted since {} is an empty JSON object. 248 Map: map[int]int{}, 249 // PointerNil is omitted since null is an empty JSON value. 250 PointerNil: nil, 251 // Pointer is omitted since "" is an empty JSON string. 252 Pointer: new(string), 253 // InterfaceNil is omitted since null is an empty JSON value. 254 InterfaceNil: nil, 255 // Interface is omitted since null is an empty JSON value. 256 Interface: (*string)(nil), 257 }) 258 if err != nil { 259 log.Fatal(err) 260 } 261 // Indent output for readability. 262 v = jsontext.Value(b) 263 v.Indent() 264 fmt.Println("OmitEmpty:", string(v)) // outputs "Bool", "Int", and "Time" 265 266 // Output: 267 // OmitZero: { 268 // "Struct": {}, 269 // "Slice": [], 270 // "Map": {}, 271 // "Pointer": "", 272 // "Interface": null 273 // } 274 // OmitEmpty: { 275 // "Bool": false, 276 // "Int": 0, 277 // "Time": "0001-01-01T00:00:00Z" 278 // } 279 } 280 281 // JSON objects can be embedded within a parent object similar to 282 // how Go structs can be embedded within a parent struct. 283 // The JSON embedding rules are similar to those of Go embedding, 284 // but operates upon the JSON namespace. 285 func Example_embeddedFields() { 286 // Base is embedded within Container. 287 type Base struct { 288 // ID is promoted into the JSON object for Container. 289 ID string 290 // Type is ignored due to presence of Container.Type. 291 Type string 292 // Time cancels out with Container.Embed.Time. 293 Time time.Time 294 } 295 // Other is embedded within Container. 296 type Other struct{ Cost float64 } 297 // Container embeds Base and Other. 298 type Container struct { 299 // Base is an embedded struct and is implicitly JSON embedded. 300 Base 301 // Type takes precedence over Base.Type. 302 Type int 303 // Embed is a named Go field, but is explicitly JSON embedded. 304 Embed struct { 305 // User is promoted into the JSON object for Container. 306 User string 307 // Time cancels out with Base.Time. 308 Time string 309 } `json:",embed"` 310 // ID does not conflict with Base.ID since the JSON name is different. 311 ID string `json:"uuid"` 312 // Other is not JSON embedded since it has an explicit JSON name. 313 Other `json:"other"` 314 } 315 316 // Format an empty Container to show what fields are JSON serializable. 317 var input Container 318 b, err := json.Marshal(&input) 319 if err != nil { 320 log.Fatal(err) 321 } 322 // Indent output for readability. 323 v := jsontext.Value(b) 324 v.Indent() 325 fmt.Println(string(v)) 326 327 // Output: 328 // { 329 // "ID": "", 330 // "Type": 0, 331 // "User": "", 332 // "uuid": "", 333 // "other": { 334 // "Cost": 0 335 // } 336 // } 337 } 338 339 // When implementing HTTP endpoints, it is common to be operating with an 340 // [io.Reader] and an [io.Writer]. The [MarshalWrite] and [UnmarshalRead] functions 341 // assist in operating on such input/output types. 342 // [UnmarshalRead] reads the entirety of the [io.Reader] to ensure that [io.EOF] 343 // is encountered without any unexpected bytes after the top-level JSON value. 344 func Example_serveHTTP() { 345 // Some global state maintained by the server. 346 var n int64 347 348 // The "add" endpoint accepts a POST request with a JSON object 349 // containing a number to atomically add to the server's global counter. 350 // It returns the updated value of the counter. 351 http.HandleFunc("/api/add", func(w http.ResponseWriter, r *http.Request) { 352 // Unmarshal the request from the client. 353 var val struct{ N int64 } 354 if err := json.UnmarshalRead(r.Body, &val); err != nil { 355 // Inability to unmarshal the input suggests a client-side problem. 356 http.Error(w, err.Error(), http.StatusBadRequest) 357 return 358 } 359 360 // Marshal a response from the server. 361 val.N = atomic.AddInt64(&n, val.N) 362 if err := json.MarshalWrite(w, &val); err != nil { 363 // Inability to marshal the output suggests a server-side problem. 364 // This error is not always observable by the client since 365 // json.MarshalWrite may have already written to the output. 366 http.Error(w, err.Error(), http.StatusInternalServerError) 367 return 368 } 369 }) 370 } 371 372 // Some Go types have a custom JSON representation where the implementation 373 // is delegated to some external package. Consequently, the "json" package 374 // will not know how to use that external implementation. 375 // For example, the [google.golang.org/protobuf/encoding/protojson] package 376 // implements JSON for all [google.golang.org/protobuf/proto.Message] types. 377 // [WithMarshalers] and [WithUnmarshalers] can be used 378 // to configure "json" and "protojson" to cooperate together. 379 func Example_protoJSON() { 380 // Let protoMessage be "google.golang.org/protobuf/proto".Message. 381 type protoMessage interface{ ProtoReflect() } 382 // Let foopbMyMessage be a concrete implementation of proto.Message. 383 type foopbMyMessage struct{ protoMessage } 384 // Let protojson be an import of "google.golang.org/protobuf/encoding/protojson". 385 var protojson struct { 386 Marshal func(protoMessage) ([]byte, error) 387 Unmarshal func([]byte, protoMessage) error 388 } 389 390 // This value mixes both non-proto.Message types and proto.Message types. 391 // It should use the "json" package to handle non-proto.Message types and 392 // should use the "protojson" package to handle proto.Message types. 393 var value struct { 394 // GoStruct does not implement proto.Message and 395 // should use the default behavior of the "json" package. 396 GoStruct struct { 397 Name string 398 Age int 399 } 400 401 // ProtoMessage implements proto.Message and 402 // should be handled using protojson.Marshal. 403 ProtoMessage *foopbMyMessage 404 } 405 406 // Marshal using protojson.Marshal for proto.Message types. 407 b, err := json.Marshal(&value, 408 // Use protojson.Marshal as a type-specific marshaler. 409 json.WithMarshalers(json.MarshalFunc(protojson.Marshal))) 410 if err != nil { 411 log.Fatal(err) 412 } 413 414 // Unmarshal using protojson.Unmarshal for proto.Message types. 415 err = json.Unmarshal(b, &value, 416 // Use protojson.Unmarshal as a type-specific unmarshaler. 417 json.WithUnmarshalers(json.UnmarshalFunc(protojson.Unmarshal))) 418 if err != nil { 419 log.Fatal(err) 420 } 421 } 422 423 // Many error types are not serializable since they tend to be Go structs 424 // without any exported fields (e.g., errors constructed with [errors.New]). 425 // Some applications, may desire to marshal an error as a JSON string 426 // even if these errors cannot be unmarshaled. 427 func ExampleWithMarshalers_errors() { 428 // Response to serialize with some Go errors encountered. 429 response := []struct { 430 Result string `json:",omitzero"` 431 Error error `json:",omitzero"` 432 }{ 433 {Result: "Oranges are a good source of Vitamin C."}, 434 {Error: &strconv.NumError{Func: "ParseUint", Num: "-1234", Err: strconv.ErrSyntax}}, 435 {Error: &os.PathError{Op: "ReadFile", Path: "/path/to/secret/file", Err: os.ErrPermission}}, 436 } 437 438 b, err := json.Marshal(&response, 439 // Intercept every attempt to marshal an error type. 440 json.WithMarshalers(json.JoinMarshalers( 441 // Suppose we consider strconv.NumError to be a safe to serialize: 442 // this type-specific marshal function intercepts this type 443 // and encodes the error message as a JSON string. 444 json.MarshalToFunc(func(enc *jsontext.Encoder, err *strconv.NumError) error { 445 return enc.WriteToken(jsontext.String(err.Error())) 446 }), 447 // Error messages may contain sensitive information that may not 448 // be appropriate to serialize. For all errors not handled above, 449 // report some generic error message. 450 json.MarshalFunc(func(error) ([]byte, error) { 451 return []byte(`"internal server error"`), nil 452 }), 453 )), 454 jsontext.Multiline(true)) // expand for readability 455 if err != nil { 456 log.Fatal(err) 457 } 458 fmt.Println(string(b)) 459 460 // Output: 461 // [ 462 // { 463 // "Result": "Oranges are a good source of Vitamin C." 464 // }, 465 // { 466 // "Error": "strconv.ParseUint: parsing \"-1234\": invalid syntax" 467 // }, 468 // { 469 // "Error": "internal server error" 470 // } 471 // ] 472 } 473 474 // In some applications, the exact precision of JSON numbers needs to be 475 // preserved when unmarshaling. This can be accomplished using a type-specific 476 // unmarshal function that intercepts all any types and pre-populates the 477 // interface value with a [jsontext.Value], which can represent a JSON number exactly. 478 func ExampleWithUnmarshalers_rawNumber() { 479 // Input with JSON numbers beyond the representation of a float64. 480 const input = `[false, 1e-1000, 3.141592653589793238462643383279, 1e+1000, true]` 481 482 var value any 483 err := json.Unmarshal([]byte(input), &value, 484 // Intercept every attempt to unmarshal into the any type. 485 json.WithUnmarshalers( 486 json.UnmarshalFromFunc(func(dec *jsontext.Decoder, val *any) error { 487 // If the next value to be decoded is a JSON number, 488 // then provide a concrete Go type to unmarshal into. 489 if dec.PeekKind() == '0' { 490 *val = jsontext.Value(nil) 491 } 492 // Return ErrUnsupported to fallback on default unmarshal behavior. 493 return errors.ErrUnsupported 494 }), 495 )) 496 if err != nil { 497 log.Fatal(err) 498 } 499 fmt.Println(value) 500 501 // Sanity check. 502 want := []any{false, jsontext.Value("1e-1000"), jsontext.Value("3.141592653589793238462643383279"), jsontext.Value("1e+1000"), true} 503 if !reflect.DeepEqual(value, want) { 504 log.Fatalf("value mismatch:\ngot %v\nwant %v", value, want) 505 } 506 507 // Output: 508 // [false 1e-1000 3.141592653589793238462643383279 1e+1000 true] 509 } 510 511 // When using JSON for parsing configuration files, 512 // the parsing logic often needs to report an error with a line and column 513 // indicating where in the input an error occurred. 514 func ExampleWithUnmarshalers_recordOffsets() { 515 // Hypothetical configuration file. 516 const input = `[ 517 {"Source": "192.168.0.100:1234", "Destination": "192.168.0.1:80"}, 518 {"Source": "192.168.0.251:4004"}, 519 {"Source": "192.168.0.165:8080", "Destination": "0.0.0.0:80"} 520 ]` 521 type Tunnel struct { 522 Source netip.AddrPort 523 Destination netip.AddrPort 524 525 // ByteOffset is populated during unmarshal with the byte offset 526 // within the JSON input of the JSON object for this Go struct. 527 ByteOffset int64 `json:"-"` // metadata to be ignored for JSON serialization 528 } 529 530 var tunnels []Tunnel 531 err := json.Unmarshal([]byte(input), &tunnels, 532 // Intercept every attempt to unmarshal into the Tunnel type. 533 json.WithUnmarshalers( 534 json.UnmarshalFromFunc(func(dec *jsontext.Decoder, tunnel *Tunnel) error { 535 // Decoder.InputOffset reports the offset after the last token, 536 // but we want to record the offset before the next token. 537 // 538 // Call Decoder.PeekKind to buffer enough to reach the next token. 539 // Add the number of leading whitespace, commas, and colons 540 // to locate the start of the next token. 541 dec.PeekKind() 542 unread := dec.UnreadBuffer() 543 n := len(unread) - len(bytes.TrimLeft(unread, " \n\r\t,:")) 544 tunnel.ByteOffset = dec.InputOffset() + int64(n) 545 546 // Return ErrUnsupported to fallback on default unmarshal behavior. 547 return errors.ErrUnsupported 548 }), 549 )) 550 if err != nil { 551 log.Fatal(err) 552 } 553 554 // lineColumn converts a byte offset into a one-indexed line and column. 555 // The offset must be within the bounds of the input. 556 lineColumn := func(input string, offset int) (line, column int) { 557 line = 1 + strings.Count(input[:offset], "\n") 558 column = 1 + offset - (strings.LastIndex(input[:offset], "\n") + len("\n")) 559 return line, column 560 } 561 562 // Verify that the configuration file is valid. 563 for _, tunnel := range tunnels { 564 if !tunnel.Source.IsValid() || !tunnel.Destination.IsValid() { 565 line, column := lineColumn(input, int(tunnel.ByteOffset)) 566 fmt.Printf("%d:%d: source and destination must both be specified", line, column) 567 } 568 } 569 570 // Output: 571 // 3:3: source and destination must both be specified 572 } 573 574 // UnmarshalDecode can be used to unmarshal a stream of whitespace-delimited 575 // JSON values. 576 func ExampleUnmarshalDecode_stream() { 577 const jsonStream = ` 578 {"Name": "Platypus", "Order": "Monotremata"} 579 {"Name": "Quoll", "Order": "Dasyuromorphia"} 580 {"Name": "Gopher", "Order": "Rodentia"} 581 ` 582 type Animal struct { 583 Name string 584 Order string 585 } 586 dec := jsontext.NewDecoder(strings.NewReader(jsonStream)) 587 for { 588 var a Animal 589 if err := json.UnmarshalDecode(dec, &a); err == io.EOF { 590 break 591 } else if err != nil { 592 log.Fatal(err) 593 } 594 fmt.Printf("%s: %s\n", a.Name, a.Order) 595 } 596 // Output: 597 // Platypus: Monotremata 598 // Quoll: Dasyuromorphia 599 // Gopher: Rodentia 600 } 601 602 // Use [jsontext.Multiline] to create multiline, indented output for more 603 // readable output for human consumption. 604 // 605 // See [jsontext.Multiline] for additional options that customize the multiline 606 // output. 607 func ExampleMarshal_multiline() { 608 type Pet struct { 609 Name string 610 Species string 611 Breed string 612 } 613 614 p := Pet{ 615 Name: "Oliver", 616 Species: "Dog", 617 Breed: "Goldendoodle", 618 } 619 620 b, err := json.Marshal(p, jsontext.Multiline(true)) 621 if err != nil { 622 log.Fatal(err) 623 } 624 625 fmt.Println(string(b)) 626 // Output: 627 // { 628 // "Name": "Oliver", 629 // "Species": "Dog", 630 // "Breed": "Goldendoodle" 631 // } 632 } 633