Skip to content

Instantly share code, notes, and snippets.

@ipedrazas
Created September 29, 2017 04:12
Show Gist options
  • Save ipedrazas/c65cadba549ea6f01dcf2590e553de54 to your computer and use it in GitHub Desktop.
Save ipedrazas/c65cadba549ea6f01dcf2590e553de54 to your computer and use it in GitHub Desktop.
Complex json in Go
package main
import "encoding/json"
import (
"fmt"
"errors"
)
type ColorfulEcosystem struct {
Things []ColoredThing `json:"things"`
}
type ColoredThing interface {
Color() string
}
type Plant struct {
MyColor string `json:"color"`
}
type Animal struct {
MyColor string `json:"color"`
}
func (p *Plant) Color() string {
return p.MyColor
}
func (p *Plant) MarshalJSON() (b []byte, e error) {
return json.Marshal(map[string]string{
"type": "plant",
"color": p.Color(),
})
}
func (a *Animal) Color() string {
return a.MyColor
}
func (a *Animal) MarshalJSON() (b []byte, e error) {
return json.Marshal(map[string]string{
"type": "animal",
"color": a.Color(),
})
}
func (ce *ColorfulEcosystem) UnmarshalJSON(b []byte) error {
var objMap map[string]*json.RawMessage
err := json.Unmarshal(b, &objMap)
if err != nil {
return err
}
var rawMessagesForColoredThings []*json.RawMessage
err = json.Unmarshal(*objMap["things"], &rawMessagesForColoredThings)
if err != nil {
return err
}
// Let's add a place to store our de-serialized Plant and Animal structs
ce.Things = make([]ColoredThing, len(rawMessagesForColoredThings))
var m map[string]string
for index, rawMessage := range rawMessagesForColoredThings {
err = json.Unmarshal(*rawMessage, &m)
if err != nil {
return err
}
// Depending on the type, we can run json.Unmarshal again on the same byte slice
// But this time, we'll pass in the appropriate struct instead of a map
if m["type"] == "plant" {
var p Plant
err := json.Unmarshal(*rawMessage, &p)
if err != nil {
return err
}
// After creating our struct, we should save it
ce.Things[index] = &p
} else if m["type"] == "animal" {
var a Animal
err := json.Unmarshal(*rawMessage, &a)
if err != nil {
return err
}
// After creating our struct, we should save it
ce.Things[index] = &a
} else {
return errors.New("Unsupported type found!")
}
}
// That's it! We made it the whole way with no errors, so we can return `nil`
return nil
}
func main() {
// First let's create some things to live in the ecosystem
fern := &Plant{MyColor: "green"}
flower := &Plant{MyColor: "purple"}
panther := &Animal{MyColor: "black"}
lizard := &Animal{MyColor: "green"}
// Then let's create a ColorfulEcosystem
colorfulEcosystem := ColorfulEcosystem{
Things: []ColoredThing{
fern,
flower,
panther,
lizard,
},
}
// prints:
// {"things":[{"color":"green","type":"plant"},{"color":"purple","type":"plant"},{"color":"black","type":"animal"},{"color":"green","type":"animal"}]}
byteSlice, _ := json.Marshal(colorfulEcosystem)
fmt.Println(string(byteSlice))
// Now let's try deserializing the JSON back to a new struct
newCE := ColorfulEcosystem{}
err := json.Unmarshal(byteSlice, &newCE)
if err != nil {
panic(err)
}
for _, colorfulThing := range newCE.Things {
fmt.Println(colorfulThing.Color())
}
}
@mikeschinkel
Copy link

mikeschinkel commented Jul 19, 2018

Hey @ipedrazas - This was super helpful, thank you so much for posting.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment