英文:
How to convert array of errors to JSON in Go
问题
我有一个错误数组(错误类型),但是当我尝试以JSON格式返回客户端时,它为空。
它是这样创建的:
var (
ErrEmptyName = errors.New("Name cannot be empty")
ErrInvalidType = errors.New("Invalid person type")
)
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
}
在我的控制器中:
err := person.Validate()
c.JSON(422, gin.H{"errors" : err})
我的输出:
{"errors":"[{}]"}
英文:
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":"[{}]"}
答案1
得分: 6
error
类型是一个带有单个 Error()
方法的接口,但它并不特定于 json
包(不会调用 Error()
方法)。
然而,error
值可能包含可以很好地进行编组的静态类型的值,或者它们可以通过实现 json.Marshaler
来定义自己的编组逻辑。仅通过调用其 Error()
方法将 error
转换为 string
意味着我们没有遵守自定义编组逻辑。
因此,我建议创建我们自己的错误切片类型,我们可以在其中实现我们的编组逻辑,应该是:
- 检查错误值是否实现了
json.Marshaler
,如果是,则让它自己进行编组 - 否则,作为后备方案,调用
error.Error()
来“获取”一个可以轻松进行编组的string
下面是示例代码:
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 知道如何自己进行编组
} else {
res[i] = e.Error() // 回退到错误字符串
}
}
return json.Marshal(res)
}
以下是如何使用它的示例:
err := person.Validate()
c.JSON(422, gin.H{"errors" : JSONErrs(err)})
让我们测试一下 JSONErrs
。我们还将使用一个实现了自定义编组逻辑的自定义错误类型:
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})
}
测试代码如下:
errs := []error{
errors.New("first"),
errors.New("second"),
MyErr{16},
}
data, err := json.Marshal(JSONErrs(errs))
fmt.Println(string(data), err)
输出结果(在 Go Playground 上尝试):
["first","second",{"Type":"MyErr","Error":"Invalid input!","AtLine":16}] <nil>
英文:
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:
- check if the error value implements
json.Marshaler
, and if so, let it marshal itself - else as a fallback case call
error.Error()
to "obtain" astring
which can easily be marshaled
This 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>
答案2
得分: 2
error
类型是一个接口,必须实现一个返回错误消息字符串的Error()
方法。具体定义可以参考这里:https://golang.org/pkg/builtin/#error。error
类型之所以是一个接口,是为了允许可以将其类型转换以获取更详细的信息。
像fmt.Println
和log.Println
这样的函数会自动解析error
类型并显示Error()
方法返回的消息,但是JSON库不会这样做。解决这个问题的最简单方法是将[]error
中的错误消息转换为[]string
,然后进行编码。
以下是使用for循环来实现这个转换的示例代码:
errs := person.Validate()
strErrors := make([]string, len(errs))
for i, err := range errs {
strErrors[i] = err.Error()
}
c.JSON(422, gin.H{"errors": strErrors})
希望对你有帮助!
英文:
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})
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论