Created
January 18, 2021 02:06
-
-
Save gildas/939b58a99c24fd9591afa28d33386669 to your computer and use it in GitHub Desktop.
How I solve "inheritance" in GO in regards to marshal/unmarshal
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" | |
"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