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  

View as plain text