Last active
January 24, 2020 18:37
-
-
Save rikonor/ad9a7e93a20416dfef9857b6a0927a08 to your computer and use it in GitHub Desktop.
Unmarshaling JSON with multiple interface implementations
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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