Skip to content

Instantly share code, notes, and snippets.

@gildas
Created January 18, 2021 02:06
Show Gist options
  • Save gildas/939b58a99c24fd9591afa28d33386669 to your computer and use it in GitHub Desktop.
Save gildas/939b58a99c24fd9591afa28d33386669 to your computer and use it in GitHub Desktop.
How I solve "inheritance" in GO in regards to marshal/unmarshal
package main
import (
"encoding/json"
"fmt"
"reflect"
)
/******************** The TypeRegistry helper ******************/
/**** from github/gildas/go-core ****/
type TypeCarrier interface {
GetType() string
}
type TypeRegistry map[string]reflect.Type
func (registry TypeRegistry) Add(classes ...TypeCarrier) TypeRegistry {
for _, class := range classes {
registry[class.GetType()] = reflect.TypeOf(class)
}
}
/************************** The Abstract Class **************************/
type Animal interface {
// Other common methods...
TypeCarrier
fmt.Stringer
}
type animalWrapper struct { // Note: This struct is private to the module
Animal Animal
}
func UnmarshalAnimal(payload []byte) (Animal, error) {
wrapper := animalWrapper{}
if err := json.Unmarshal(payload, &wrapper); err != nil {
return nil, err
}
return wrapper.Animal, nil
}
func (wrapper *animalWrapper) UnmarshalJSON(payload []byte) (err Error) {
header := struct {
Type string `json:"type"`
}{}
if err = json.Unmarshal(payload, &header); err != nil {
return err
}
var value Animal
registry := TypeRegistry{}.Add(
Dog{},
)
if valueType, found := registry[header.Type]; found {
value = reflect.New(valueType).Interface().(Message)
} else {
return fmt.Errorf("Unsupported Type: %s", header.type)
}
if err = json.Unmarshal(payload, &value); err != nil {
return
}
wrapper.Message = value // This is a pointer to the actual class, e.g.: *Dog
return
}
/******************* The Dog ****************************************/
type Dog struct {
Color string `json:"color"`
}
func (dog Dog) GetType() string {
return "dog"
}
// implements fmt.Stringer
func (dog Dog) String() string {
return dog.Color + " dog"
}
func (dog Dog) MarhalJSON() ([]byte, error) {
type surrogate Dog
return json.Marshal(struct {
Type string `json:"type"`
surrogate
}{
Type: dog.GetType(),
surrogate: surrogate(dog),
})
}
func (dog *Dog) UnmarshalJSON(payload []byte) (err error) {
type surrogate Dog
var inner struct {
Type string `json:"type"`
surrogate
}
if err = json.Unmarshal(payload, &inner); err != nil {
return err
}
*dog = Dog(inner.surrogate)
return
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment