Something went wrong. Try again.
forked from tailscale/tailscale
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172// Copyright (c) Tailscale Inc & contributors// 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/71497package 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}