diff options
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" + // } +} |
