Source file src/encoding/json/v2/example_orderedobject_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  	"fmt"
    11  	"log"
    12  	"reflect"
    13  
    14  	"encoding/json/jsontext"
    15  	"encoding/json/v2"
    16  )
    17  
    18  // OrderedObject is an ordered sequence of name/value members in a JSON object.
    19  //
    20  // RFC 8259 defines an object as an "unordered collection".
    21  // JSON implementations need not make "ordering of object members visible"
    22  // to applications nor will they agree on the semantic meaning of an object if
    23  // "the names within an object are not unique". For maximum compatibility,
    24  // applications should avoid relying on ordering or duplicity of object names.
    25  type OrderedObject[V any] []ObjectMember[V]
    26  
    27  // ObjectMember is a JSON object member.
    28  type ObjectMember[V any] struct {
    29  	Name  string
    30  	Value V
    31  }
    32  
    33  // MarshalJSONTo encodes obj as a JSON object into enc.
    34  func (obj *OrderedObject[V]) MarshalJSONTo(enc *jsontext.Encoder) error {
    35  	if err := enc.WriteToken(jsontext.BeginObject); err != nil {
    36  		return err
    37  	}
    38  	for i := range *obj {
    39  		member := &(*obj)[i]
    40  		if err := json.MarshalEncode(enc, &member.Name); err != nil {
    41  			return err
    42  		}
    43  		if err := json.MarshalEncode(enc, &member.Value); err != nil {
    44  			return err
    45  		}
    46  	}
    47  	if err := enc.WriteToken(jsontext.EndObject); err != nil {
    48  		return err
    49  	}
    50  	return nil
    51  }
    52  
    53  // UnmarshalJSONFrom decodes a JSON object from dec into obj.
    54  func (obj *OrderedObject[V]) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
    55  	if k := dec.PeekKind(); k != '{' {
    56  		return fmt.Errorf("expected object start, but encountered %v", k)
    57  	}
    58  	if _, err := dec.ReadToken(); err != nil {
    59  		return err
    60  	}
    61  	for dec.PeekKind() != '}' {
    62  		*obj = append(*obj, ObjectMember[V]{})
    63  		member := &(*obj)[len(*obj)-1]
    64  		if err := json.UnmarshalDecode(dec, &member.Name); err != nil {
    65  			return err
    66  		}
    67  		if err := json.UnmarshalDecode(dec, &member.Value); err != nil {
    68  			return err
    69  		}
    70  	}
    71  	if _, err := dec.ReadToken(); err != nil {
    72  		return err
    73  	}
    74  	return nil
    75  }
    76  
    77  // The exact order of JSON object can be preserved through the use of a
    78  // specialized type that implements [MarshalerTo] and [UnmarshalerFrom].
    79  func Example_orderedObject() {
    80  	// Round-trip marshal and unmarshal an ordered object.
    81  	// We expect the order and duplicity of JSON object members to be preserved.
    82  	// Specify jsontext.AllowDuplicateNames since this object contains "fizz" twice.
    83  	want := OrderedObject[string]{
    84  		{"fizz", "buzz"},
    85  		{"hello", "world"},
    86  		{"fizz", "wuzz"},
    87  	}
    88  	b, err := json.Marshal(&want, jsontext.AllowDuplicateNames(true))
    89  	if err != nil {
    90  		log.Fatal(err)
    91  	}
    92  	var got OrderedObject[string]
    93  	err = json.Unmarshal(b, &got, jsontext.AllowDuplicateNames(true))
    94  	if err != nil {
    95  		log.Fatal(err)
    96  	}
    97  
    98  	// Sanity check.
    99  	if !reflect.DeepEqual(got, want) {
   100  		log.Fatalf("roundtrip mismatch: got %v, want %v", got, want)
   101  	}
   102  
   103  	// Print the serialized JSON object.
   104  	(*jsontext.Value)(&b).Indent() // indent for readability
   105  	fmt.Println(string(b))
   106  
   107  	// Output:
   108  	// {
   109  	// 	"fizz": "buzz",
   110  	// 	"hello": "world",
   111  	// 	"fizz": "wuzz"
   112  	// }
   113  }
   114  

View as plain text