Created
September 29, 2017 04:12
-
-
Save ipedrazas/c65cadba549ea6f01dcf2590e553de54 to your computer and use it in GitHub Desktop.
Complex json in Go
This file contains 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" | |
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()) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hey @ipedrazas - This was super helpful, thank you so much for posting.