Home > database >  How to accommodate for 2 different types of the same field when parsing JSON
How to accommodate for 2 different types of the same field when parsing JSON

Time:05-03

I'm working with a JSON rest api that can return 2 different types of the same field.

Here's an example response from the api:

"result": {
    "value": {
        "error": "Invalid data",
        "logs": [],
    }
},

But sometimes it also returns this

"result": {
    "value": {
        "error": {
             "msg": "Rate limit exceeded"
           },
        "logs": []
    }
},

As you can see the error field can change to an object.

Here's how i have defined the type in my code

type ApiResponse struct {
    Result struct {
        Value struct {
            Error string `json:"error"`
            Logs []string `json:"logs"`
        } `json:"value"`
    } `json:"result"`
}

But since the type of error can change, this sometimes causes issues when JSON unmarshaling: json: cannot unmarshal object into Go struct field .result.value.error of type string

How can i deal with this scenario?

CodePudding user response:

You can implement the json.Unmarshaler interface to customize how the error field will be decoded.

type ErrorMessage struct {
    Text string
}

func (e *ErrorMessage) UnmarshalJSON(data []byte) error {
    if len(data) == 0 || string(data) == "null" {
        return nil
    }
    if data[0] == '"' && data[len(data)-1] == '"' { // string?
        return json.Unmarshal(data, &e.Text)
    }
    if data[0] == '{' && data[len(data)-1] == '}' { // object?
        type T struct {
            Text string `json:"msg"`
        }
        return json.Unmarshal(data, (*T)(e))
    }
    return fmt.Errorf("unsupported error message type")
}

Then update the ApiResponse type's Error field to be of type ErrorMessage.

type ApiResponse struct {
    Result struct {
        Value struct {
            Error ErrorMessage `json:"error"`
            Logs  []string     `json:"logs"`
        } `json:"value"`
    } `json:"result"`
}

https://go.dev/play/p/U7FBiA9qB54

CodePudding user response:

As interface Error is defined as string, you need a custom type error object implementing the Error interface - which returns msg probably. Then create your own unmarshalling method which handles the possible json strings.

CodePudding user response:

Define your structure as follows:

type ApiResponse struct {
    Result struct {
        Value struct {
            Error interface{}`json:"error"`
            Logs  []string   `json:"logs"`
        } `json:"value"`
    } `json:"result"`
}

Unmarshall into a variable of type ApiResponse (lets assume data). The type of Error then can be checked as follows:

switch v := data.Result.Value.Error.(type) {
    case string:
        // 1st case
    case map[string]interface{}:
        // 2nd case, read key "msg"
    default:
        fmt.Printf("I don't know about type %T!\n", v)
}
  • Related