| // 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 != '{' { |
| // The [json] package automatically populates relevant fields |
| // in a [json.SemanticError] to provide additional context. |
| return &json.SemanticError{JSONKind: 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" |
| // } |
| } |