0

I have an interface that's implemented by a few structs. On one of my methods, I instantiate the struct base on a given string and then parse a json into the defined struct. To do so, I have implemented UnmarshalJSON but it's never being called.

Interface:

type vehicle interface {
    vehicleType() string
    numberOfWheels() int
    EngineType() string
}

Two structs that implement the interface:

type truck struct {
    loadCapacity  int
    capacityUnits string
}

func (t truck) vehicleType() string {
    return "Truck"
}

func (t truck) numberOfWheels() int {
    return 6
}

func (t truck) EngineType() string {
    return "Gasoline"
}

func (t *truck) UnmarshalJSON(data []byte) error {
    fmt.Println("Here in custom unmarshal")
    return nil
}

// -------------------------------------------
type ev struct {
    capacityInKWh int
}

func (e ev) vehicleType() string {
    return "Electric Vehicle"
}

func (e ev) numberOfWheels() int {
    return 4
}

func (e ev) EngineType() string {
    return "Electric"
}

func (e ev) Capacity() int {
    return e.capacityInKWh
}

Notice that my truck struct also implements the custom UnmarshalJSON.

Now, in main I'm trying to do the following:

func main() {
    jsonData := []byte(`{"loadCapacity": 1000, "capacityUnits": "lb"}`)
    vehicleType := "truck"

    processVehicle(vehicleType, jsonData)
}

func processVehicle(vehicleType string, data []byte) {
    var myVehicle vehicle

    fmt.Println("Processing vehicle...")

    switch vehicleType {
    case "truck":
        myVehicle = truck{}
    }

    err := json.Unmarshal(data, &myVehicle)
    if err != nil {
        fmt.Println(err.Error())
        return
    }

    fmt.Println(myVehicle)
}

But I'm getting the following error:

json: cannot unmarshal object into Go value of type main.vehicle

I know there's no actual body in the implemented UnmarshalJSON for truck but I'd expect at least the message to be printed.

What am I doing wrong?

HerbertRedford
  • 230
  • 4
  • 14
  • 2
    So `truck` implementes the `json.Unmarshaler` interface, but you're unmarshaling into type `vehicle`, which is just an interface, so that doesn't work. You need to unmarshal into a concrete `truck` value. – Jonathan Hall Nov 27 '20 at 14:01
  • @Flimzy but I won't know what type of vehicle `processVehicle` is going to receive. It's there a way around it? I mean, I don't want to implement multiple `switch` all over the place – HerbertRedford Nov 27 '20 at 14:04
  • 1
    Then whatever encloses `vehicle` must make the decision about which type to unmarshal into. – JimB Nov 27 '20 at 14:08
  • 1
    You need some sort of switch statement (or equivalent logic) _somewhere_. There is no magic in Go. – Jonathan Hall Nov 27 '20 at 14:10
  • In [this video](https://www.youtube.com/watch?v=vsN11YAEJHY&t=18m52s) I talk about using a container object for a similar situation, which @JimB seems to have eluded to. – Jonathan Hall Nov 27 '20 at 14:15

1 Answers1

0

The JSONUnmarshal function is on the pointer receiver. Use a pointer value to access that method.

switch vehicleType {
case "truck":
    myVehicle = &truck{}  // <-- add & here
}

err := json.Unmarshal(data, myVehicle) // <-- & not needed here

Run it on the Go Playground.

blimzy
  • 9
  • 1