dongqiaochi2711 2014-01-21 00:02
浏览 66
已采纳

遍历Struct中的字符串字段

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):

  • 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 + "
    "
           }
           return "Errors were encountered during form processing: " + fullError
       }
    

Thanks in advance.

  • 写回答

2条回答 默认 最新

  • dongnanbi4942 2014-01-21 01:05
    关注

    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
    ", 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
    ", ms.A, ms.B, ms.C, ms.I, ms.D, ms.J)
    }
    

    (Playground link)

    There are two caveats here:

    1. You need a pointer to what you're going to change. If you have a value, you'll need to return the modified result.

    2. 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).

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 孟德尔随机化怎样画共定位分析图
  • ¥18 模拟电路问题解答有偿速度
  • ¥15 CST仿真别人的模型结果仿真结果S参数完全不对
  • ¥15 误删注册表文件致win10无法开启
  • ¥15 请问在阿里云服务器中怎么利用数据库制作网站
  • ¥60 ESP32怎么烧录自启动程序
  • ¥50 html2canvas超出滚动条不显示
  • ¥15 java业务性能问题求解(sql,业务设计相关)
  • ¥15 52810 尾椎c三个a 写蓝牙地址
  • ¥15 elmos524.33 eeprom的读写问题