// Copyright 2026 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" "maps" "slices" "encoding/json/jsontext" "encoding/json/v2" ) // IntSet is a simple set of numbers, implemented using a map. type IntSet map[int]struct{} // MarshalJSONTo encodes s as a JSON array into enc. func (s *IntSet) MarshalJSONTo(enc *jsontext.Encoder) error { // Encode the set as a JSON array. if err := enc.WriteToken(jsontext.BeginArray); err != nil { return err } keys := maps.Keys(*s) // Go map iteration order is non-deterministic. If the caller has // requested deterministic output then we must sort the entries. deterministic, _ := json.GetOption(enc.Options(), json.Deterministic) if deterministic { keys = slices.Values(slices.Sorted(keys)) } for k := range keys { // Call MarshalEncode instead of Write methods on Encoder so // that the "json" package can automatically handle any options // that may be relevant to the representation of k. // In this case, StringifyNumbers may affect whether k is // quoted or not. if err := json.MarshalEncode(enc, k); err != nil { return err } } if err := enc.WriteToken(jsontext.EndArray); err != nil { return err } return nil } // UnmarshalJSONFrom decodes a JSON array from dec into s. func (s *IntSet) UnmarshalJSONFrom(dec *jsontext.Decoder) error { // Consume start of array. 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() != ']' { var v int // See comment in MarshalJSONTo. if err := json.UnmarshalDecode(dec, &v); err != nil { return err } if *s == nil { *s = IntSet{} } (*s)[v] = struct{}{} } // Consume end of array. if _, err := dec.ReadToken(); err != nil { return err } return nil } // Custom types may define custom marshal behavior with [MarshalerTo]. func ExampleMarshalerTo() { set := IntSet{ 1: {}, 2: {}, 3: {}, } b, err := json.Marshal(&set, json.Deterministic(true)) if err != nil { log.Fatal(err) } // Indent output for readability. v := jsontext.Value(b) v.Indent() fmt.Println(string(v)) // Output: // [ // 1, // 2, // 3 // ] } // Custom types may define custom unmarshal behavior with [UnmarshalerFrom]. func ExampleUnmarshalerFrom() { s := "[1,2,3]" var set IntSet err := json.Unmarshal([]byte(s), &set) if err != nil { log.Fatal(err) } fmt.Println(set) // Output: // map[1:{} 2:{} 3:{}] }