| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171 |
- // Copyright (c) Tailscale Inc & AUTHORS
- // SPDX-License-Identifier: BSD-3-Clause
- // Package jsonx contains helper types and functionality to use with
- // [github.com/go-json-experiment/json], which is positioned to be
- // merged into the Go standard library as [encoding/json/v2].
- //
- // See https://go.dev/issues/71497
- package jsonx
- import (
- "errors"
- "fmt"
- "reflect"
- "github.com/go-json-experiment/json"
- "github.com/go-json-experiment/json/jsontext"
- )
- var (
- errUnknownTypeName = errors.New("unknown type name")
- errNonSingularValue = errors.New("dynamic value must only have exactly one member")
- )
- // MakeInterfaceCoders constructs a pair of marshal and unmarshal functions
- // to serialize a Go interface type T. A bijective mapping for the set
- // of concrete types that implement T is provided,
- // where the key is a stable type name to use in the JSON representation,
- // while the value is any value of a concrete type that implements T.
- // By convention, only the zero value of concrete types is passed.
- //
- // The JSON representation for a dynamic value is a JSON object
- // with a single member, where the member name is the type name,
- // and the value is the JSON representation for the Go value.
- // For example, the JSON serialization for a concrete type named Foo
- // would be {"Foo": ...}, where ... is the JSON representation
- // of the concrete value of the Foo type.
- //
- // Example instantiation:
- //
- // // Interface is a union type implemented by [FooType] and [BarType].
- // type Interface interface { ... }
- //
- // var interfaceCoders = MakeInterfaceCoders(map[string]Interface{
- // "FooType": FooType{},
- // "BarType": (*BarType)(nil),
- // })
- //
- // The pair of Marshal and Unmarshal functions can be used with the [json]
- // package with either type-specified or caller-specified serialization.
- // The result of this constructor is usually stored into a global variable.
- //
- // Example usage with type-specified serialization:
- //
- // // InterfaceWrapper is a concrete type that wraps [Interface].
- // // It extends [Interface] to implement
- // // [json.MarshalerTo] and [json.UnmarshalerFrom].
- // type InterfaceWrapper struct{ Interface }
- //
- // func (w InterfaceWrapper) MarshalJSONTo(enc *jsontext.Encoder) error {
- // return interfaceCoders.Marshal(enc, &w.Interface)
- // }
- //
- // func (w *InterfaceWrapper) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
- // return interfaceCoders.Unmarshal(dec, &w.Interface)
- // }
- //
- // Example usage with caller-specified serialization:
- //
- // var opts json.Options = json.JoinOptions(
- // json.WithMarshalers(json.MarshalToFunc(interfaceCoders.Marshal)),
- // json.WithUnmarshalers(json.UnmarshalFromFunc(interfaceCoders.Unmarshal)),
- // )
- //
- // var v Interface
- // ... := json.Marshal(v, opts)
- // ... := json.Unmarshal(&v, opts)
- //
- // The function panics if T is not a named interface kind,
- // or if valuesByName contains distinct entries with the same concrete type.
- func MakeInterfaceCoders[T any](valuesByName map[string]T) (c struct {
- Marshal func(*jsontext.Encoder, *T) error
- Unmarshal func(*jsontext.Decoder, *T) error
- }) {
- // Verify that T is a named interface.
- switch t := reflect.TypeFor[T](); {
- case t.Kind() != reflect.Interface:
- panic(fmt.Sprintf("%v must be an interface kind", t))
- case t.Name() == "":
- panic(fmt.Sprintf("%v must be a named type", t))
- }
- // Construct a bijective mapping of names to types.
- typesByName := make(map[string]reflect.Type)
- namesByType := make(map[reflect.Type]string)
- for name, value := range valuesByName {
- t := reflect.TypeOf(value)
- if t == nil {
- panic(fmt.Sprintf("nil value for %s", name))
- }
- if name2, ok := namesByType[t]; ok {
- panic(fmt.Sprintf("type %v cannot have multiple names %s and %v", t, name, name2))
- }
- typesByName[name] = t
- namesByType[t] = name
- }
- // Construct the marshal and unmarshal functions.
- c.Marshal = func(enc *jsontext.Encoder, val *T) error {
- t := reflect.TypeOf(*val)
- if t == nil {
- return enc.WriteToken(jsontext.Null)
- }
- name := namesByType[t]
- if name == "" {
- return fmt.Errorf("Go type %v: %w", t, errUnknownTypeName)
- }
- if err := enc.WriteToken(jsontext.BeginObject); err != nil {
- return err
- }
- if err := enc.WriteToken(jsontext.String(name)); err != nil {
- return err
- }
- if err := json.MarshalEncode(enc, *val); err != nil {
- return err
- }
- if err := enc.WriteToken(jsontext.EndObject); err != nil {
- return err
- }
- return nil
- }
- c.Unmarshal = func(dec *jsontext.Decoder, val *T) error {
- switch tok, err := dec.ReadToken(); {
- case err != nil:
- return err
- case tok.Kind() == 'n':
- var zero T
- *val = zero // store nil interface value for JSON null
- return nil
- case tok.Kind() != '{':
- return &json.SemanticError{JSONKind: tok.Kind(), GoType: reflect.TypeFor[T]()}
- }
- var v reflect.Value
- switch tok, err := dec.ReadToken(); {
- case err != nil:
- return err
- case tok.Kind() != '"':
- return errNonSingularValue
- default:
- t := typesByName[tok.String()]
- if t == nil {
- return errUnknownTypeName
- }
- v = reflect.New(t)
- }
- if err := json.UnmarshalDecode(dec, v.Interface()); err != nil {
- return err
- }
- *val = v.Elem().Interface().(T)
- switch tok, err := dec.ReadToken(); {
- case err != nil:
- return err
- case tok.Kind() != '}':
- return errNonSingularValue
- }
- return nil
- }
- return c
- }
|