diff options
| author | Damien Neil <dneil@google.com> | 2025-04-11 14:19:51 -0700 |
|---|---|---|
| committer | Gopher Robot <gobot@golang.org> | 2025-04-18 08:24:07 -0700 |
| commit | 0e17905793cb5e0acc323a0cdf3733199d93976a (patch) | |
| tree | fec117ceb6b56866e6c51e6acd72901cf91717ce /src/encoding/json/v2/example_orderedobject_test.go | |
| parent | c889004615b40535ebd5054cbcf2deebdb3a299a (diff) | |
| download | go-0e17905793cb5e0acc323a0cdf3733199d93976a.tar.xz | |
encoding/json: add json/v2 with GOEXPERIMENT=jsonv2 guard
This imports the proposed new v2 JSON API implemented in
github.com/go-json-experiment/json as of commit
d3c622f1b874954c355e60c8e6b6baa5f60d2fed.
When GOEXPERIMENT=jsonv2 is set, the encoding/json/v2 and
encoding/jsontext packages are visible, the encoding/json
package is implemented in terms of encoding/json/v2, and
the encoding/json package include various additional APIs.
(See #71497 for details.)
When GOEXPERIMENT=jsonv2 is not set, the new API is not
present and the encoding/json package is unchanged.
The experimental API is not bound by the Go compatibility
promise and is expected to evolve as updates are made to
the json/v2 proposal.
The contents of encoding/json/internal/jsontest/testdata
are compressed with zstd v1.5.7 with the -19 option.
Fixes #71845
For #71497
Change-Id: Ib8c94e5f0586b6aaa22833190b41cf6ef59f4f01
Reviewed-on: https://go-review.googlesource.com/c/go/+/665796
Auto-Submit: Damien Neil <dneil@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Michael Pratt <mpratt@google.com>
Reviewed-by: Joseph Tsai <joetsai@digital-static.net>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Diffstat (limited to 'src/encoding/json/v2/example_orderedobject_test.go')
| -rw-r--r-- | src/encoding/json/v2/example_orderedobject_test.go | 113 |
1 files changed, 113 insertions, 0 deletions
diff --git a/src/encoding/json/v2/example_orderedobject_test.go b/src/encoding/json/v2/example_orderedobject_test.go new file mode 100644 index 0000000000..d68782f725 --- /dev/null +++ b/src/encoding/json/v2/example_orderedobject_test.go @@ -0,0 +1,113 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build goexperiment.jsonv2 + +package json_test + +import ( + "fmt" + "log" + "reflect" + + "encoding/json/jsontext" + "encoding/json/v2" +) + +// OrderedObject is an ordered sequence of name/value members in a JSON object. +// +// RFC 8259 defines an object as an "unordered collection". +// JSON implementations need not make "ordering of object members visible" +// to applications nor will they agree on the semantic meaning of an object if +// "the names within an object are not unique". For maximum compatibility, +// applications should avoid relying on ordering or duplicity of object names. +type OrderedObject[V any] []ObjectMember[V] + +// ObjectMember is a JSON object member. +type ObjectMember[V any] struct { + Name string + Value V +} + +// MarshalJSONTo encodes obj as a JSON object into enc. +func (obj *OrderedObject[V]) MarshalJSONTo(enc *jsontext.Encoder) error { + if err := enc.WriteToken(jsontext.BeginObject); err != nil { + return err + } + for i := range *obj { + member := &(*obj)[i] + if err := json.MarshalEncode(enc, &member.Name); err != nil { + return err + } + if err := json.MarshalEncode(enc, &member.Value); err != nil { + return err + } + } + if err := enc.WriteToken(jsontext.EndObject); err != nil { + return err + } + return nil +} + +// UnmarshalJSONFrom decodes a JSON object from dec into obj. +func (obj *OrderedObject[V]) UnmarshalJSONFrom(dec *jsontext.Decoder) error { + if k := dec.PeekKind(); k != '{' { + return fmt.Errorf("expected object start, but encountered %v", k) + } + if _, err := dec.ReadToken(); err != nil { + return err + } + for dec.PeekKind() != '}' { + *obj = append(*obj, ObjectMember[V]{}) + member := &(*obj)[len(*obj)-1] + if err := json.UnmarshalDecode(dec, &member.Name); err != nil { + return err + } + if err := json.UnmarshalDecode(dec, &member.Value); err != nil { + return err + } + } + if _, err := dec.ReadToken(); err != nil { + return err + } + return nil +} + +// The exact order of JSON object can be preserved through the use of a +// specialized type that implements [MarshalerTo] and [UnmarshalerFrom]. +func Example_orderedObject() { + // Round-trip marshal and unmarshal an ordered object. + // We expect the order and duplicity of JSON object members to be preserved. + // Specify jsontext.AllowDuplicateNames since this object contains "fizz" twice. + want := OrderedObject[string]{ + {"fizz", "buzz"}, + {"hello", "world"}, + {"fizz", "wuzz"}, + } + b, err := json.Marshal(&want, jsontext.AllowDuplicateNames(true)) + if err != nil { + log.Fatal(err) + } + var got OrderedObject[string] + err = json.Unmarshal(b, &got, jsontext.AllowDuplicateNames(true)) + if err != nil { + log.Fatal(err) + } + + // Sanity check. + if !reflect.DeepEqual(got, want) { + log.Fatalf("roundtrip mismatch: got %v, want %v", got, want) + } + + // Print the serialized JSON object. + (*jsontext.Value)(&b).Indent() // indent for readability + fmt.Println(string(b)) + + // Output: + // { + // "fizz": "buzz", + // "hello": "world", + // "fizz": "wuzz" + // } +} |
