简体   繁体   中英

Convert slice of errors to a slice of structs in golang

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 error s 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 (ie 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.

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, eg

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM