Skip to content

Instantly share code, notes, and snippets.

@alexkappa
Last active March 14, 2022 15:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save alexkappa/4b541a712dc06c047a38b005178978b5 to your computer and use it in GitHub Desktop.
Save alexkappa/4b541a712dc06c047a38b005178978b5 to your computer and use it in GitHub Desktop.
type FleetVehicle interface {
MakeAndModel() string
}
type Vehicle struct {
Type string `json:"type"`
Make string `json:"make"`
Model string `json:"model"`
}
func (v Vehicle) MakeAndModel() string {
return fmt.Sprintf("%s %s", v.Make, v.Model)
}
type Car struct {
Vehicle
SeatingCapacity int `json:"seatingCapacity"`
TopSpeed float64 `json:"topSpeed"`
}
type Truck struct {
Vehicle
PayloadCapacity float64 `json:"payloadCapacity"`
}
type Bike struct {
Vehicle
TopSpeed float64 `json:"topSpeed"`
}
type Fleet struct {
Vehicles []FleetVehicle `json:"-"`
RawVehicles []json.RawMessage `json:"vehicles"`
}
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))
}
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 FleetVehicle
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
}
import (
"encoding/json"
"testing"
)
func TestJSONMarshal(t *testing.T) {
fleet := &Fleet{
Vehicles: []FleetVehicle{
&Car{Vehicle{"car", "Mercedes-Benz", "AMG C 63"}, 4, 250},
&Bike{Vehicle{"bike", "Triumph", "Bonneville T120"}, 193.12},
},
}
b, err := json.MarshalIndent(&fleet, "", " ")
if err != nil {
t.Fatal(err)
}
t.Logf("%s\n", b)
}
func TestJSONUnmarshal(t *testing.T) {
const body = `{
"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}
]
}`
var f *Fleet
err := json.Unmarshal([]byte(body), &f)
if err != nil {
t.Fatal(err)
}
for _, vehicle := range f.Vehicles {
if _, ok := vehicle.(FleetVehicle); !ok {
t.Fatal("expected type to implement FleetVehicle")
}
switch v := vehicle.(type) {
case *Car:
t.Logf("%s has a seating capacity of %d and a top speed of %.2f km/h\n",
v.MakeAndModel(),
v.SeatingCapacity,
v.TopSpeed)
case *Truck:
t.Logf("%s has a payload capacity of %.2f kg\n",
v.MakeAndModel(),
v.PayloadCapacity)
case *Bike:
t.Logf("%s has a top speed of %.2f km/h\n",
v.MakeAndModel(),
v.TopSpeed)
}
}
}
=== RUN TestJSONMarshal
--- PASS: TestJSONMarshal (0.00s)
json_test.go:22: {
"vehicles": [
{
"type": "car",
"make": "Mercedes-Benz",
"model": "AMG C 63",
"seatingCapacity": 4,
"topSpeed": 250
},
{
"type": "bike",
"make": "Triumph",
"model": "Bonneville T120",
"topSpeed": 193.12
}
]
}
=== RUN TestJSONUnmarshal
--- PASS: TestJSONUnmarshal (0.00s)
json_test.go:45: BMW M3 has a seating capacity of 4 and a top speed of 250.00 km/h
json_test.go:50: Volvo FH has a payload capacity of 40000.00 kg
json_test.go:54: Yamaha YZF-R1 has a top speed of 293.00 km/h
PASS
@alexkappa
Copy link
Author

alexkappa commented Mar 14, 2022

Hi @inikolaev, as far as I'm aware there is no such functionality in encoding/json. My attempt was intended at replicating this functionality, but of course, it requires additional effort. One of the third-party JSON packages perhaps?

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