I have an array of errors (type of error), but when I try to return the client in JSON format, it arrives empty.
It was created this way:
var (
ErrEmptyName = errors.New("Nome não pode ser vázio")
ErrInvalidType = errors.New("Tipo de pessoa inválido")
)
func (p Person) Validate() []error {
var errors []error
if govalidator.IsNull(p.Name) {
errors = append(errors, ErrEmptyName)
}
if p.Type != "F" && p.Type != "J" {
errors = append(errors, ErrInvalidType)
}
return errors
)
In my Controller:
err := person.Validate()
c.JSON(422, gin.H{"errors" : err})
My Output:
{"errors":"[{}]"}
The error
type is an interface with a single Error()
method, but it is not special to the json
package (the Error()
method is not called on it).
However, error
values may hold values of static types which may be nicely marshalable, or they may define their own marshaling logic by implementing json.Marshaler
. Simply converting an error
to string
by calling its Error()
method means we're not honoring the custom marshaling logic.
So I would suggest to create our own error slice type, on which we can implement our marshaling logic which should be:
json.Marshaler
, and if so, let it marshal itselferror.Error()
to "obtain" a string
which can easily be marshaledThis is how it could look like:
type JSONErrs []error
func (je JSONErrs) MarshalJSON() ([]byte, error) {
res := make([]interface{}, len(je))
for i, e := range je {
if _, ok := e.(json.Marshaler); ok {
res[i] = e // e knows how to marshal itself
} else {
res[i] = e.Error() // Fallback to the error string
}
}
return json.Marshal(res)
}
And this is how you can use it:
err := person.Validate()
c.JSON(422, gin.H{"errors" : JSONErrs(err)})
Let's test our JSONErrs
. We're also use a custom error type which implements custom marshaling logic:
type MyErr struct{ line int }
func (me MyErr) Error() string { return "Invalid input!" }
func (me MyErr) MarshalJSON() ([]byte, error) {
return json.Marshal(
struct {
Type, Error string
AtLine int
}{"MyErr", me.Error(), me.line})
}
And the test code:
errs := []error{
errors.New("first"),
errors.New("second"),
MyErr{16},
}
data, err := json.Marshal(JSONErrs(errs))
fmt.Println(string(data), err)
Output (try it on the Go Playground):
["first","second",{"Type":"MyErr","Error":"Invalid input!","AtLine":16}] <nil>
An error
type is an interface which must implement an Error()
method that returns an error message as a string. This is defined here: https://golang.org/pkg/builtin/#error. The reason why the error
type is an interface, is to allow for error
types that can be type casted to retrieve more detailed information.
Functions like fmt.Println
and log.Println
automatically resolves error
types to display the message from Error()
, the JSON library however, does not. The simplest way to get around this problem is by converting the error messages in []error
to a []string
and encoding that.
Here's some example code to do that with a for loop.
errs := person.Validate()
strErrors := make([]string, len(errs))
for i, err := range errs {
strErrors[i] = err.Error()
}
c.JSON(422, gin.H{"errors" : strErrors})
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