迭代结构中的字符串字段 [英] Iterate Over String Fields in Struct
问题描述
我正在寻找迭代结构的字符串字段,所以我可以做一些清理/验证(使用 strings.TrimSpace
, strings.Trim
等)。
现在我有一个混乱的开关盒,它不是真正可扩展的,因为这不是在我的应用程序(一个web表单)的热点地区,它似乎利用了 reflect
在这里是一个不错的选择。
然而,如何实现这一点,我遇到了一些障碍,反射文档对我来说有点困惑(我一直在挖掘其他一些验证包,但它们太重量级了+我正在使用已经解码的部分已经有大猩猩/架构):
$ b
- 迭代结构
- 对于每个字段的字符串类型,从
字符串
包中应用我需要的任何内容即field = strings.TrimSpace(field)
- 如果存在field.Tag.Get(max),我们将使用该值(strconv.Atoi,然后是unicode.Rune CountInString)
提供一个与错误接口类型兼容的错误片
类型FormError []字符串
类型列表结构{
标题字符串`max:50`
位置字符串`max:100`
描述字符串`max:10000`
ExpiryDate time.Time
RenderedDesc template.HTML
联系人字符串`max:255`
}
//遍历我们的结构,尽可能修复空格/格式
//并返回遇到的错误
func(l * Listing)Validate()错误{
typ:= l .Elem()。Type()
var invalid FormError
for i = 0;我< typ.NumField(); i ++ {
//迭代字段
//对于string类型的结构字段,字段= strings.TrimSpace(字段)
// if field.Tag.Get(max)!= {
//检查最大长度/转换为int / utf8.RuneCountInString
如果超过最大长度,invalid = append(invalid,errormsg)
}
如果len(无效)> 0 {
返回无效
}
返回nil
}
func(f FormError)Error()string {
var fullError string
for _,v:= range f {
fullError = + v +\\\
}
return表单中遇到错误处理:+ fullError
提前致谢
解决方案你想要的主要是reflect.Value上的方法,称为
NumFields )int
和Field(int)
。您唯一真正想要的是字符串检查和SetString
方法。b
$ b $ ,B,C字符串
I int
D字符串
J int
}
func main(){
ms:= MyStruct { Green,Eggs,and,2,Ham,15}
//现在打印出来,以便我们看到差异
fmt.Printf(%s%s%我们需要一个指针,我们需要一个指针,这个指针指向一个指针,以便我们可以通过反射设置值
msValuePtr:= reflect.ValueOf(& ms)
msValue:= msValuePtr.Elem()
for i:= 0;我< msValue.NumField(); i ++ {
field:= msValue.Field(i)
//忽略与字符串不同的字段
如果field.Type()!= reflect.TypeOf(){
continue
}
str:= field.Interface()。(string)
str = strings.TrimSpace(str)
field.SetString(str)
}
fmt.Printf(%s%s%s%d%s%d \\\
,ms.A,ms.B,ms .C,ms.I,ms.D,ms.J)
}
<一个href =http://play.golang.org/p/DRixAydsas>(游乐场链接)
这里有两个注意事项: p>
-
你需要一个指向你要改变的指针。如果你有一个值,你需要返回修改后的结果。
试试修改未导出的字段通常会导致反应恐慌。如果您打算修改未导出的字段,请确保在包内执行此操作。
,如果您需要不同的行为(取决于类型),则可以使用switch语句或类型开关(在field.Interface()返回的值上)。 - 对于每个字段的字符串类型,从
- Iterate over the struct
- For each field of type string, apply whatever I need to from the
strings
package i.e.field = strings.TrimSpace(field)
- If there exists a field.Tag.Get("max"), we'll use that value (strconv.Atoi, then unicode.RuneCountInString)
Provide an error slice that's also compatible with the error interface type
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 }
编辑:至于标签的行为,你似乎已经明白了。一旦你有字段并检查它是一个字符串,你可以使用 field.Tag.Get(max)
并解析它。
Edit2:我在标签上发生了一个小错误。标签是结构的reflect.Type的一部分,所以让它们可以使用(这有点冗长) msValue.Type()。Field(i).Tag.Get(最大)
a>您在评论中发布的代码与一个工作标签获取)。
I'm looking to iterate over the string fields of a struct so I can do some clean-up/validation (with strings.TrimSpace
, strings.Trim
, etc).
Right now I have a messy switch-case that's not really scalable, and as this isn't in a hot spot of my application (a web form) it seems leveraging reflect
is a good choice here.
I'm at a bit of a roadblock for how to implement this however, and the reflect docs are a little confusing to me (I've been digging through some other validation packages, but they're way too heavyweight + I'm using gorilla/schema for the unmarshalling part already):
Thanks in advance.
What you want is primarily the methods on reflect.Value called NumFields() int
and Field(int)
. The only thing you're really missing is the string check and SetString
method.
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)
}
There are two caveats here:
You need a pointer to what you're going to change. If you have a value, you'll need to return the modified result.
Attempts to modify unexported fields generally will cause reflect to panic. If you plan on modifying unexported fields, make sure to do this trick inside the package.
This code is rather flexible, you can use switch statements or type switches (on the value returned by field.Interface()) if you need differing behavior depending on the type.
Edit: As for the tag behavior, you seem to already have that figured out. Once you have field and have checked that it's a string, you can just use field.Tag.Get("max")
and parse it from there.
Edit2: I made a small error on the tag. Tags are part of the reflect.Type of a struct, so to get them you can use (this is a bit long-winded) msValue.Type().Field(i).Tag.Get("max")
(Playground version of the code you posted in the comments with a working Tag get).
这篇关于迭代结构中的字符串字段的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!