I'm trying to encode some JSON for a REST api, everything is working fine except for some errors. For example, with this struct:
type TemplateResponse struct {
Message string
Error error
Template Template
}
Encoded with this data:
res := TemplateResponse{"Template not found.", fmt.Errorf("There is no template on this host with the name " + vars["name"]), Template{}}
json.NewEncoder(w).Encode(res)
Returns:
{
"Message": "Template not found.",
"Error": {},
"Template": {
"Name": "",
"Disabled": false,
"Path": "",
"Version": ""
}
}
I'm getting this seemingly randomly across my application, where 'error' types are being returned as empty. Any ideas?
Thanks!
To encode JSON data we use the Marshal function. Only data structures that can be represented as valid JSON will be encoded: JSON objects only support strings as keys; to encode a Go map type it must be of the form map[string]T (where T is any Go type supported by the json package).
Golang provides multiple APIs to work with JSON including to and from built-in and custom data types using the encoding/json package. To parse JSON, we use the Unmarshal() function in package encoding/json to unpack or decode the data from JSON to a struct.
To unmarshal a JSON array into a slice, Unmarshal resets the slice length to zero and then appends each element to the slice. As a special case, to unmarshal an empty JSON array into a slice, Unmarshal replaces the slice with a new empty slice.
error is a built-in interface type in Go. An error variable represents any value that can describe itself as a string . The following is the error interface declaration: type error interface { Error() string.
Because error
is just an interface. It may hold a value of any concrete type that implements it.
In your example you used fmt.Errorf()
to create an error
value. That calls errors.New()
which returns a pointer to a value of the unexported errors.errorString
struct. Its definition is:
type errorString struct {
s string
}
This struct value will be marshaled, but since it has no exported fields (only exported fields are marshaled), it will be an empty JSON object: {}
.
The "fix" is: don't marshal values of "general" interfaces, relying on that the dynamic values can be marshaled into JSON meaningfully. Instead you should add a field that stores the error string (the result of error.Error()
), and omit the Error error
field from marshaling, e.g.:
type TemplateResponse struct {
Message string
Error error `json:"-"`
ErrorMsg string
Template Template
}
Of course then you also need to set / fill the ErrorMsg
field before marshaling.
Or if you don't need to store the error
value in the struct, remove that field completely:
type TemplateResponse struct {
Message string
ErrorMsg string
Template Template
}
If you still want to keep the Error error
field (and not the ErrorMsg
field), then you need to implement a custom marshaling logic by implementing the json.Marshaler
interface where you can "convert" the error
value to a meaningful string
for example (or into another value that can be marshaled properly).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With