Skip to content

Instantly share code, notes, and snippets.

@rikonor
Last active January 24, 2020 18:37
Show Gist options
  • Select an option

  • Save rikonor/ad9a7e93a20416dfef9857b6a0927a08 to your computer and use it in GitHub Desktop.

Select an option

Save rikonor/ad9a7e93a20416dfef9857b6a0927a08 to your computer and use it in GitHub Desktop.
Unmarshaling JSON with multiple interface implementations
package main
import (
"encoding/json"
"errors"
"fmt"
"log"
"reflect"
)
func main() {
b := []byte(`
[
{
"type": "person",
"name": "joe",
"friends": [
{
"type": "person",
"name": "eli"
}
]
},
{
"type": "dog",
"name": "bud"
},
{
"type": "cat",
"name": "kitty"
}
]
`)
var ns Namers
if err := json.Unmarshal(b, &ns); err != nil {
log.Fatal(err)
}
for _, n := range ns {
fmt.Println(n.GetName())
}
}
type Namer interface {
GetName() string
}
type Person struct {
Name string
Friends Namers
}
func (p *Person) GetName() string {
return fmt.Sprintf("Person: %s has %d friends.", p.Name, len(p.Friends))
}
type Dog struct {
Name string
Friends Namers
}
func (d *Dog) GetName() string {
return fmt.Sprintf("Dog: %s has %d friends.", d.Name, len(d.Friends))
}
type Cat string
func (c *Cat) UnmarshalJSON(b []byte) error {
v := struct{ Name string }{}
if err := json.Unmarshal(b, &v); err != nil {
return err
}
*c = Cat(v.Name)
return nil
}
func (c *Cat) GetName() string {
return "Cat: " + string(*c)
}
type Namers []Namer
func (ns *Namers) UnmarshalJSON(b []byte) error {
vs := []map[string]interface{}{}
if err := json.Unmarshal(b, &vs); err != nil {
return err
}
typs := map[string]reflect.Type{
"person": reflect.TypeOf(Person{}),
"dog": reflect.TypeOf(Dog{}),
"cat": reflect.TypeOf(Cat("")),
}
for _, v := range vs {
typ, ok := v["type"].(string)
if !ok {
return errors.New("missing type")
}
rtyp, ok := typs[typ]
if !ok {
return fmt.Errorf("unknown type: %s", typ)
}
bs, err := json.Marshal(v)
if err != nil {
return err
}
n := reflect.New(rtyp).Interface().(Namer)
if err := json.Unmarshal(bs, &n); err != nil {
return err
}
*ns = append(*ns, n)
}
return nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment