JSON polymorphism in Go

Serializing and deserializing polymorphic JSON into Go structs

Introduction

Example

{
"vehicles": [
{
"type": "car",
"make": "BMW",
"model": "M3",
"seatingCapacity": 4,
"topSpeed": 250
},
{
"type": "truck",
"make": "Volvo",
"model": "FH",
"payloadCapacity": 40000
},
{
"type": "bike",
"make": "Yamaha",
"model": "YZF-R1",
"topSpeed": 293
}
]
}

Types

type Vehicle struct {
Type string `json:"type"`
Make string `json:"make"`
Model string `json:"model"`
}
type Car struct {
SeatingCapacity int `json:"seatingCapacity"`
TopSpeed float64 `json:"topSpeed"`
Vehicle
}
type Truck struct {
PayloadCapacity float64 `json:"payloadCapacity"`
Vehicle
}
type Bike struct {
TopSpeed float64 `json:"topSpeed"`
Vehicle
}
type Fleet struct {
Vehicles []interface{} `json:"vehicles"`
}
type Fleet struct {
Vehicles []interface{} `json:"-"`
RawVehicles []json.RawMessage `json:"vehicles"`
}

Methods

func (f *Fleet) UnmarshalJSON(b []byte) error {    type fleet Fleet    err := json.Unmarshal(b, (*fleet)(f))
if err != nil {
return err
}

for _, raw := range f.RawVehicles {
var v Vehicle
err = json.Unmarshal(raw, &v)
if err != nil {
return err
}
var i interface{}
switch v.Type {
case "car":
i = &Car{}
case "truck":
i = &Truck{}
case "bike":
i = &Bike{}
default:
return errors.New("unknown vehicle type")
}
err = json.Unmarshal(raw, i)
if err != nil {
return err
}
f.Vehicles = append(f.Vehicles, i)
}
return nil
}
func (f *Fleet) MarshalJSON() ([]byte, error) {

type fleet Fleet
if f.Vehicles != nil {
for _, v := range f.Vehicles {
b, err := json.Marshal(v)
if err != nil {
return nil, err
}
f.RawVehicles = append(f.RawVehicles, b)
}
}
return json.Marshal((*fleet)(f))
}
var f *Fleet
err := json.Unmarshal([]byte(body), &f)
if err != nil {
t.Fatal(err)
}
for _, vehicle := range f.Vehicles { switch v := vehicle.(type) {
case *Car:
fmt.Printf("%s %s has a seating capacity of %d and a top speed of %.2f km/h\n",
v.Make,
v.Model,
v.SeatingCapacity,
v.TopSpeed)
case *Truck:
fmt.Printf("%s %s has a payload capacity of %.2f kg\n",
v.Make,
v.Model,
v.PayloadCapacity)
case *Bike:
fmt.Printf("%s %s has a top speed of %.2f km/h\n",
v.Make,
v.Model,
v.TopSpeed)
}
}
type FleetVehicle interface {
MakeAndModel() string
}
type Fleet struct {
Vehicles []FleetVehicle `json:"-"`
RawVehicles []json.RawMessage `json:"vehicles"`
}

Conclusion

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store