将错误的切片转换为结构体的切片在Go语言中。

huangapple go评论156阅读模式
英文:

Convert slice of errors to a slice of structs in golang

问题

我正在使用Golang的validate库来进行输入错误检查,作为API的一部分(一个用于学习目的的愚蠢演示API)。

当进行验证时,会返回一个error切片。实际上,该切片由validate库的结构体BadField组成,其结构如下:

type BadField struct {
    Field string
    Err   error
}

func (b BadField) Error() string {
    return fmt.Sprintf("field %s is invalid: %v", b.Field, b.Err)
}

我想传递一个更具体的切片,而不是[]error,所以我想要[]BadField,这样我就可以访问Field的值。

到目前为止,我找不到一种从一个类型转换为另一个类型的方法。也许没有这样的方法(由于Go和切片的特性)。也许有一个可以为我做这个转换的包。

我的初始实现

我想到的方法是循环遍历切片,并逐个转换每个元素。

errors := valueWithBadStuff.Validate()

validationErrors := make([]validate.BadField, len(errors))
for _, err := range errors {
    validationError, ok := err.(validate.BadField)
    if !ok {
        panic("badarghfiremyeyes") // 仅用于演示目的
    }
    validationErrors = append(validationErrors, validationError)
}

这种方法对于一个“简单”的操作来说感觉有点冗长,也许有一种更符合Go习惯的方式?或者有一种更好的方式?

背景知识,我目前的意图是将验证错误的切片作为JSON对象数组传回给客户端,其中包含Field名称和错误消息(例如,对于一个虚构的age字段:["field_name": "age", "不能小于0"])。

在上面的循环之后,我进行了更多的转换,生成了一个带有json标签的结构体切片,实际上将发送给客户端。额外的转换可能是重复和无意义的,但是现在,它纯粹是一个学习的部分,我可能会在一两个小时内进行重构。

英文:

I'm using the Golang validate library to do some input error checking as part of an API (a silly demo API for learning purposes).

When one performs the validation a slice of errors is returned. In reality, the slice is made up of the validate library's struct BadField, which looks like this:

type BadField struct {
	Field string
	Err   error
}

func (b BadField) Error() string {
	return fmt.Sprintf("field %s is invalid: %v", b.Field, b.Err)
}

I'd like to pass around a more-specific slice, so rather than []error I would like have []BadField so that I can access the Field value.

So far I can't find a way of casting/converting from one to the other. Maybe there isn't one (due to the nature of go and slices). Maybe there's a package that will do this for me.

My initial implementation

The way I've come up with is to loop through the slice and cast each element individually.

errors := valueWithBadStuff.Validate()

validationErrors := make([]validate.BadField, len(errors))
for _, err := range errors {
	validationError, ok := err.(validate.BadField)
	if !ok {
		panic("badarghfiremyeyes") // S/O purposes only
	}
	validationErrors = append(validationErrors, validationError)
}

Which feels kinda long for something "simple" but perhaps there's a more go idiomatic way? Or a nicer way?

For background, my intention (at the moment) is to take the slice of validation errors and pipe it back to the client as an array of JSON objects with the Field name and the error message (i.e. for a fictional age field: ["field_name": "age", "Cannot be less than 0"])

Just after the loop above I do more conversion to generate a slice of structs that are tagged with json that will actually go the client. The extra conversion may be duplication and pointless but, right now, it's purely a learning piece and I'll probably refactor it in an hour or two.

答案1

得分: 3

这里没有真正的“更好”的方法来做这个。要转换一个切片,你基本上要做你已经发现的事情。

如果你只是将这些错误返回给客户端,你可能可以避免完全需要进行类型转换。

实现JSON Marshaler 接口,你可以使你的类型自动以你想要的格式输出JSON。例如,对于你上面给出的格式,可以这样做:

func (e BadField) MarshalJSON() ([]byte, error) {
   return json.Marshal([]string{"field_name", e.Field, e.Err.Error()})
}

不过我怀疑你可能更希望得到类似下面的响应:

[  
   {  
      "field":"age",
      "error":"msg1"
   },
   {  
      "field":"name",
      "error":"msg2"
   }
]

要做到这一点,你可以简单地在结构定义中添加JSON标签,例如:

type BadField struct {
    Field string `json:"field"`
    Err   error  `json:"error"`
}

这意味着在包含BadField实例的[]error切片上调用json.Marshal将得到上述JSON。

阅读更多关于JSON和Go的信息可能会有所帮助。

PS 考虑一下你想要你的方法是值接收器还是指针接收器

英文:

There's not really a "nicer" way of doing this. To convert a slice you have to basically do what you've already discovered.

If you are simply returning these errors to a client, you could probably avoid the need to typecast this at all.

Implement the JSON Marshaler interface and you can make your type will automatically output the JSON in the format you desire. For example, for the format you gave above this would be:

func (e BadField)  MarshalJSON() ([]byte, error) {
   return json.Marshal([]string{"field_name",e.Field,e.Err.Error()})
}

I suspect however that you would probably rather have a response something like:

[  
   {  
      "field":"age",
      "error":"msg1"
   },
   {  
      "field":"name",
      "error":"msg2"
   }
]

To do this, you could simply add the JSON tags to the struct definition, e.g.

type BadField struct {
    Field string `json:"field"`
    Err   error `json:"error"`
} 

This would mean calling json.Marshal on a slice of []error which contains BadField instances would result in the JSON above.

It might be helpful to read more about JSON & Go

PS Consider if you want your methods to be value or pointer receivers

huangapple
  • 本文由 发表于 2017年7月28日 16:11:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/45368204.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定