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