Skip to content

Instantly share code, notes, and snippets.

@spikeekips
Created Dec 27, 2019
Embed
What would you like to do?
package main
import (
"encoding/json"
"fmt"
"reflect"
)
type Something interface{}
type Something1 struct {
Aaa, Bbb string
}
type Something2 struct {
Ccc, Ddd string
}
var _ Something = (*Something1)(nil)
var _ Something = (*Something2)(nil)
type Container struct {
Type string `json:"type"`
Value Something `json:"value"`
}
func (c *Container) UnmarshalJSON(data []byte) error {
value, err := UnmarshalCustomValue(data, "type", "value", map[string]reflect.Type{
"something1": reflect.TypeOf(Something1{}),
"something2": reflect.TypeOf(Something2{}),
})
if err != nil {
return err
}
c.Value = value
return nil
}
func UnmarshalCustomValue(data []byte, typeJsonField, valueJsonField string, customTypes map[string]reflect.Type) (interface{}, error) {
m := map[string]interface{}{}
if err := json.Unmarshal(data, &m); err != nil {
return nil, err
}
typeName := m[typeJsonField].(string)
var value Something
if ty, found := customTypes[typeName]; found {
value = reflect.New(ty).Interface().(Something)
}
valueBytes, err := json.Marshal(m[valueJsonField])
if err != nil {
return nil, err
}
if err = json.Unmarshal(valueBytes, &value); err != nil {
return nil, err
}
return value, nil
}
var _ json.Unmarshaler = (*Container)(nil)
func panicIfErr(err error) {
if err != nil {
panic(err.Error())
}
}
func main() {
testUnmarshalling(`{"type":"something1","value":{"Aaa": "a"}}`)
testUnmarshalling(`{"type":"something2","value":{"Ccc": "a"}}`)
}
func testUnmarshalling(jsonStr string) {
var container Container
err := json.Unmarshal([]byte(jsonStr), &container)
panicIfErr(err)
fmt.Printf("container=%+v\n", container)
fmt.Printf("value=%#v\n", container.Value)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment