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)
}