8 "github.com/pkg/errors"
11 type jsonMapper struct {
12 kindToType map[string]reflect.Type
13 typeToKind map[reflect.Type]string
16 func newJsonMapper(base interface{}) *jsonMapper {
18 kindToType: map[string]reflect.Type{},
19 typeToKind: map[reflect.Type]string{},
23 // ToJSON is a convenience method to serialize with encoding/json
24 func ToJSON(o interface{}) ([]byte, error) {
25 d, err := json.MarshalIndent(o, "", " ")
26 return d, errors.WithStack(err)
29 // FromJSON is a convenience method to deserialize with encoding/json
30 func FromJSON(d []byte, o interface{}) error {
31 return errors.WithStack(
35 // registerImplementation allows you to register multiple concrete types.
37 // Returns itself to allow calls to be chained
38 func (m *jsonMapper) registerImplementation(data interface{}, kind string, b byte) {
39 typ := reflect.TypeOf(data)
40 m.kindToType[kind] = typ
41 m.typeToKind[typ] = kind
44 // getTarget returns a pointer to an allocated object of the proper kind
45 func (m *jsonMapper) getTarget(kind string) (interface{}, error) {
46 typ, ok := m.kindToType[kind]
48 return nil, errors.Errorf("Unmarshaling into unknown type: %s", kind)
50 target := reflect.New(typ).Interface()
54 func (m *jsonMapper) getKind(obj interface{}) (string, error) {
55 typ := reflect.TypeOf(obj)
56 kind, ok := m.typeToKind[typ]
58 return "", errors.Errorf("Marshalling from unknown type: %#v", obj)
63 // FromJSON will deserialize the output of ToJSON for every registered
64 // implementation of the interface
65 func (m *jsonMapper) FromJSON(data []byte) (interface{}, error) {
66 // handle null specially
67 if bytes.Equal(data, []byte("null")) {
71 Data: &json.RawMessage{},
73 err := json.Unmarshal(data, &e)
77 // switch on the type, then unmarshal into that
78 bytes := *e.Data.(*json.RawMessage)
79 res, err := m.getTarget(e.Kind)
83 err = json.Unmarshal(bytes, &res)
84 // getTarget returned a pointer for Unmarshall, now dereference it
85 res = reflect.ValueOf(res).Elem().Interface()
89 // ToJson will serialize a registered implementation into a format like:
96 // this allows us to properly deserialize with FromJSON
97 func (m *jsonMapper) ToJSON(data interface{}) ([]byte, error) {
98 // handle null specially
100 return []byte("null"), nil
102 raw, err := json.Marshal(data)
104 return nil, errors.WithStack(err)
106 kind, err := m.getKind(data)
110 msg := json.RawMessage(raw)
115 return json.Marshal(e)
118 // envelope lets us switch on type
119 type envelope struct {
120 Kind string `json:"type"`
121 Data interface{} `json:"data"`