Problem:
Cannot deserialize to an interface.
Example:
Pet is an interface with a method called Type() that returns the type of pet. Dog and Cat implement Type() and return "dog" and "cat" respectively. House has one pet.
package main
import (
"encoding/json"
"fmt"
)
func main() {
// House has a pet dog.
h := House {
Pet: &Dog{},
}
b, err := json.Marshal(h)
if err != nil {
panic(err)
}
h2 := House{}
err = json.Unmarshal(b, &h2)
if err != nil {
panic(err)
}
// Expect dog
fmt.Println(h2.Pet.Type())
// House has a pet cat.
h = House {
Pet: &Cat{},
}
b, err = json.Marshal(h)
if err != nil {
panic(err)
}
h2 = House{}
err = json.Unmarshal(b, &h2)
if err != nil {
panic(err)
}
// Expect cat
fmt.Println(h2.Pet.Type())
}
type House struct {
Pet Pet
}
type Pet interface {
Type() string
}
type Dog struct {
}
func (d *Dog) Type() string {
return `dog`
}
type Cat struct {
}
func (c *Cat) Type() string {
return `cat`
}
https://play.golang.org/p/mYYK7L7IQK
When you run this code, you get this error "json: cannot unmarshal object into Go struct field House.Pet of type main.Pet" because it doesn't how to unmarshal it an interface Pet.
Solution:
package main
import (
"encoding/json"
"fmt"
)
func main() {
// House has a pet dog.
h := House {
Pet: &Dog{},
}
b, err := json.Marshal(h)
if err != nil {
panic(err)
}
h2 := House{}
err = json.Unmarshal(b, &h2)
if err != nil {
panic(err)
}
// Expect dog
fmt.Println(h2.Pet.Type())
// House has a pet cat.
h = House {
Pet: &Cat{},
}
b, err = json.Marshal(h)
if err != nil {
panic(err)
}
h2 = House{}
err = json.Unmarshal(b, &h2)
if err != nil {
panic(err)
}
// Expect cat
fmt.Println(h2.Pet.Type())
}
type House struct {
Pet Pet
}
func (h *House) UnmarshalJSON(data []byte) error {
var m map[string]interface{}
err := json.Unmarshal(data, &m)
if err != nil {
return err
}
pet := m["Pet"]
if pet == nil {
return fmt.Errorf("cannot unmarshal pet")
}
m = pet.(map[string]interface{})
data, err = json.Marshal(m)
if err != nil {
return err
}
name := m["PetName"]
if name == nil {
return fmt.Errorf("cannot unmarshal pet")
}
p, ok := pets[name.(string)]
if !ok {
return fmt.Errorf("cannot unmarshal pet %q", name)
}
err = json.Unmarshal(data, p)
if err != nil {
return err
}
h.Pet = p
return nil
}
var pets = map[string]Pet {`Dog`: &Dog{}, `Cat`: &Cat{}}
type Pet interface {
Type() string
}
type Dog struct {
}
func (d *Dog) Type() string {
return `dog`
}
func (d *Dog) MarshalJSON() ([]byte, error) {
type MyCopy Dog
return json.Marshal(&struct {
PetName string
*MyCopy
}{
PetName: `Dog`,
MyCopy: (*MyCopy)(d),
})
}
type Cat struct {
}
func (c *Cat) Type() string {
return `cat`
}
func (c *Cat) MarshalJSON() ([]byte, error) {
type MyCopy Cat
return json.Marshal(&struct {
PetName string
*MyCopy
}{
PetName: `Cat`,
MyCopy: (*MyCopy)(c),
})
}
https://play.golang.org/p/Wc_eMVtOpy