How do you loop through the fields in a Golang struct to get and set values in an extensible way?

前端 未结 5 1597
孤城傲影
孤城傲影 2021-02-07 06:42

I have a struct Person.

type Person struct {
    Firstname string       
    Lastname  string       
    Years     uint8       
}

Then I have t

相关标签:
5条回答
  • 2021-02-07 07:18

    Here is the solution f2.Set(reflect.Value(f)) is the key here

    package main
    
       import (
        "fmt"
        "reflect"
       )
    
       func main() {
        type T struct {
            A int
            B string
        }
        t := T{23, "skidoo"}
        t2:= T{}
        s := reflect.ValueOf(&t).Elem()
        s2 := reflect.ValueOf(&t2).Elem()
        typeOfT := s.Type()
        fmt.Println("t=",t)
        fmt.Println("t2=",t2)
    
        for i := 0; i < s.NumField(); i++ {
            f := s.Field(i)
            f2:= s2.Field(i)
            fmt.Printf("%d: %s %s = %v\n", i,
                typeOfT.Field(i).Name, f.Type(), f.Interface())
            fmt.Printf("%d: %s %s = %v\n", i,
                typeOfT.Field(i).Name, f2.Type(), f2.Interface())
            f2.Set(reflect.Value(f))
            fmt.Printf("%d: %s %s = %v\n", i,
                typeOfT.Field(i).Name, f2.Type(), f2.Interface())
    
        }
        fmt.Println("t=",t)
        fmt.Println("t2=",t2)
    }
    
    Output:
    
    t= {23 skidoo}
    t2= {0 }
    0: A int = 23
    0: A int = 0
    0: A int = 23
    1: B string = skidoo
    1: B string = 
    1: B string = skidoo
    t= {23 skidoo}
    t2= {23 skidoo}
    

    http://play.golang.org/p/UKFMBxfbZD

    0 讨论(0)
  • 2021-02-07 07:22

    I don't even know how many ways this can go wrong...

    package main
    
    import (
        "fmt"
        "encoding/json"
    )
    
    type Serializable interface {
        fromMap(map[string]interface{}) error
        toMap() (map[string]interface{}, error)
    }
    
    type Person struct {
        Firstname string       
        Lastname  string       
        Years     uint8       
    }
    
    func (p *Person) fromMap(m map[string]interface{}) error {
        b, err := json.Marshal(m)
        if err != nil {
            return err
        }
    
        if err := json.Unmarshal(b, p); err != nil {
            return err
        }
        return nil
    }
    
    func (p Person) toMap() (map[string]interface{}, error) {
        b, err := json.Marshal(p)
        if err != nil {
            return nil, err
        }
        m := map[string]interface{}{}
        if err := json.Unmarshal(b, &m); err != nil {
            return nil, err
        }
        return m, nil
    }
    
    func copy(p1 Serializable, p2 Serializable) error {
    
        m1, err := p1.toMap()
        if err != nil {
            return err
        }
    
        m2, err := p2.toMap()
        if err != nil {
            return err
        }
    
        for k := range m1 {
            m2[k] = m1[k]
        }
    
        if err := p2.fromMap(m2); err != nil {
            return err
        }
        return nil
    }
    
    func main() {
        p1 := Person{
            "Mary",
            "Jane",
            26,
        }
    
        p2 := Person {
            "Random",
            "Lady",
            26,
        }
    
        if err := copy(&p1, &p2); err != nil {
            fmt.Printf("ERR: %s\n", err.Error())
            return
        }
    
        fmt.Printf("%v\n", p2)
    
    }
    
    0 讨论(0)
  • 2021-02-07 07:25

    You should use a map[string]interface{} instead, gonna be much faster (although still not as fast as you used the proper logic with actual structs).

    package main
    
    import "fmt"
    
    type Object map[string]interface{}
    
    var m = Object{
        "Firstname": "name",
        "Lastname":  "",
        "years":     uint8(10),
    }
    
    func main() {
        var cp = Object{}
        for k, v := range m {
            if s, ok := v.(string); ok && s != "" {
                cp[k] = s
            } else if ui, ok := v.(uint8); ok {
                cp[k] = ui
            }
        }
        fmt.Printf("%#v\n", cp)
    }
    
    0 讨论(0)
  • 2021-02-07 07:28

    Use reflect.ValueOf() to convert to concrete type. After that you could use reflect.Value.SetString to set the value you want.

    structValue := FooBar{Foo: "foo", Bar: 10}
    fields := reflect.TypeOf(structValue)
    values := reflect.ValueOf(structValue)
    
    num := fields.NumField()
    
    for i := 0; i < num; i++ {
        field := fields.Field(i)
        value := values.Field(i)
        fmt.Print("Type:", field.Type, ",", field.Name, "=", value, "\n")
    
        switch value.Kind() {
        case reflect.String:
            v := value.String()
            fmt.Print(v, "\n")
        case reflect.Int:
            v := strconv.FormatInt(value.Int(), 10)
            fmt.Print(v, "\n")
        case reflect.Int32:
            v := strconv.FormatInt(value.Int(), 10)
            fmt.Print(v, "\n")
        case reflect.Int64:
            v := strconv.FormatInt(value.Int(), 10)
            fmt.Print(v, "\n")
        default:
            assert.Fail(t, "Not support type of struct")
        }
    }
    
    0 讨论(0)
  • 2021-02-07 07:37

    Reflection should be all you need. This seems similar (though not identical) to "deep copy" semantics, which has been implemented at https://godoc.org/github.com/getlantern/deepcopy

    You should be able to adapt that to your needs, or at least take some ideas from it.

    0 讨论(0)
提交回复
热议问题