Search code examples
gostructinterface

interface conversion: interface {} is map[string]interface {}, not


I have a few structs defined like

type EventPayload struct {
    IsAdded        bool   `json:"is_added"`
    IsUpdated      bool   `json:"is_updated"`
    IsDeleted      bool   `json:"is_deleted"`
}

type HubEvent struct {
    Time      string      `json:"time"`
    Payload   interface{} `json:"payload"`
}

type EventRecord struct {
    InputEventPayload HubEvent `json:"inputEventPayload"`
    EventID           string         `json:"eventId"`
}

Now, I have an array of EventRecord on which I need to iterate. I need to do 2 things:

  1. For every eventRecord in the array, I need to check if InputEventPayload exists or not
  2. If it does, I need to convert InputEventPayload.Payload interface to EventPayload struct

I have created a test file to test my code where I'm passing the EventRecords array

[]EventRecord{
    {
        EventID: "eventID-1",
    },
    {
        EventID: "eventID-2",
        InputEventPayload: HubEvent{
        Payload: EventPayload{
            IsAdded: true,
            IsUpdated: false,
        },
            },
    },
}

My code looks like this

for _, record := range EventRecords {

    // check if the InputEventPayload exists in the record
    if record.InputEventPayload != (HubEvent{}) {

        // typecast
        payload := record.InputEventPayload.Payload.(EventPayload)

        // do something with the payload
    }
}

I don't know if this is the right way to check if the field InputEventPayload exists or not. Secondly, when the code tries to typecast, it gives me the following error

interface conversion: interface {} is map[string]interface {}, not EventPayload

The important thing here is that I cannot change the schema of any structs here


Solution

  • The problem (also with your test data) is that you deserialize JSON into interface{}, making an object map[string]interface{} per default.

    One solution might be to serialize and deserialize it again:

        for _, record := range EventRecords {
            // check if the InputEventPayload exists in the record
            if record.InputEventPayload.Payload != nil {
                tmp, err := json.Marshal(record.InputEventPayload.Payload)
                if err != nil {
                    panic(err) // handle the error properly in your code
                }
                var event EventPayload
                if err := json.Unmarshal(tmp, &event); err != nil {
                    panic(err) // handle the error properly in your code
                }
                fmt.Println(event)
            }
        }
    

    Better would be to use the correct structure directly, which you mention you can not do:

    type HubEvent struct {
        Time    string        `json:"time"`
        Payload *EventPayload `json:"payload"`
    }
    

    To suggest better solutions, we might need to know more about your problem and why the structure is the way it is, and is not changeable.