我期待遍历一个struct的字符串字段,所以我可以做一些清理/验证(与strings.TrimSpace,strings.Trim等).
现在我有一个凌乱的交换机案例,它不是真正可扩展的,因为这不是我的应用程序(网络表单)的热点,它似乎利用reflect是一个很好的选择.
然而,我对如何实现这个问题有一些障碍,而且反射文档对我来说有点混乱(我一直在挖掘其他一些验证包,但它们太重量级了+我正在使用gorilla/schema for the unmarshalling part已经):
strings包中应用我需要的任何内容,即field = strings.TrimSpace(field)提供与错误接口类型兼容的错误切片
type FormError []string
type Listing struct {
Title string `max:"50"`
Location string `max:"100"`
Description string `max:"10000"`
ExpiryDate time.Time
RenderedDesc template.HTML
Contact string `max:"255"`
}
// Iterate over our struct, fix whitespace/formatting where possible
// and return errors encountered
func (l *Listing) Validate() error {
typ := l.Elem().Type()
var invalid FormError
for i = 0; i < typ.NumField(); i++ {
// Iterate over fields
// For StructFields of type string, field = strings.TrimSpace(field)
// if field.Tag.Get("max") != "" {
// check max length/convert to int/utf8.RuneCountInString
if max length exceeded, invalid = append(invalid, "errormsg")
}
if len(invalid) > 0 {
return invalid
}
return nil
}
func (f FormError) Error() string {
var fullError string
for _, v := range f {
fullError =+ v + "\n"
}
return "Errors were encountered during form processing: " + fullError
}
Run Code Online (Sandbox Code Playgroud)提前致谢.
Lin*_*ope 12
你想要的主要是关于reflect.Value的方法NumFields() int和Field(int).你唯一真正缺少的是字符串检查和SetString方法.
package main
import "fmt"
import "reflect"
import "strings"
type MyStruct struct {
A,B,C string
I int
D string
J int
}
func main() {
ms := MyStruct{"Green ", " Eggs", " and ", 2, " Ham ", 15}
// Print it out now so we can see the difference
fmt.Printf("%s%s%s%d%s%d\n", ms.A, ms.B, ms.C, ms.I, ms.D, ms.J)
// We need a pointer so that we can set the value via reflection
msValuePtr := reflect.ValueOf(&ms)
msValue := msValuePtr.Elem()
for i := 0; i < msValue.NumField(); i++ {
field := msValue.Field(i)
// Ignore fields that don't have the same type as a string
if field.Type() != reflect.TypeOf("") {
continue
}
str := field.Interface().(string)
str = strings.TrimSpace(str)
field.SetString(str)
}
fmt.Printf("%s%s%s%d%s%d\n", ms.A, ms.B, ms.C, ms.I, ms.D, ms.J)
}
Run Code Online (Sandbox Code Playgroud)
这里有两个警告:
你需要一个指向你要改变的东西的指针.如果您有值,则需要返回修改后的结果.
尝试修改未导出的字段通常会导致反映出现恐慌.如果您计划修改未导出的字段,请确保在包内执行此操作.
此代码相当灵活,如果根据类型需要不同的行为,可以使用switch语句或类型开关(在field.Interface()返回的值上).
编辑:至于标签行为,你似乎已经弄明白了.一旦你有字段并检查它是一个字符串,你可以field.Tag.Get("max")从那里使用和解析它.
Edit2:我在标签上犯了一个小错误.标签是结构的reflect.Type的一部分,所以为了得到它们你可以使用(这有点啰嗦)msValue.Type().Field(i).Tag.Get("max")
(您在评论中发布的代码的Playground版本,带有工作标记get).
我受到了打击,但自从我开始工作以来,这是一个解决方案:
type FormError []*string
type Listing struct {
Title string `max:"50"`
Location string `max:"100"`
Description string `max:"10000"`
ExpiryDate time.Time
RenderedDesc template.HTML
Contact string `max:"255"`
}
// Iterate over our struct, fix whitespace/formatting where possible
// and return errors encountered
func (l *Listing) Validate() error {
listingType := reflect.TypeOf(*l)
listingValue := reflect.ValueOf(l)
listingElem := listingValue.Elem()
var invalid FormError = []*string{}
// Iterate over fields
for i := 0; i < listingElem.NumField(); i++ {
fieldValue := listingElem.Field(i)
// For StructFields of type string, field = strings.TrimSpace(field)
if fieldValue.Type().Name() == "string" {
newFieldValue := strings.TrimSpace(fieldValue.Interface().(string))
fieldValue.SetString(newFieldValue)
fieldType := listingType.Field(i)
maxLengthStr := fieldType.Tag.Get("max")
if maxLengthStr != "" {
maxLength, err := strconv.Atoi(maxLengthStr)
if err != nil {
panic("Field 'max' must be an integer")
}
// check max length/convert to int/utf8.RuneCountInString
if utf8.RuneCountInString(newFieldValue) > maxLength {
// if max length exceeded, invalid = append(invalid, "errormsg")
invalidMessage := `"`+fieldType.Name+`" is too long (max allowed: `+maxLengthStr+`)`
invalid = append(invalid, &invalidMessage)
}
}
}
}
if len(invalid) > 0 {
return invalid
}
return nil
}
func (f FormError) Error() string {
var fullError string
for _, v := range f {
fullError = *v + "\n"
}
return "Errors were encountered during form processing: " + fullError
}
Run Code Online (Sandbox Code Playgroud)
我看到你问过如何做标签.Reflection有两个组件:类型和值.标签与类型相关联,因此您必须单独获取它而不是字段:listingType := reflect.TypeOf(*l).然后,您可以从中获取索引字段和标记.