0%

go reflect get and set struct value

有时候我们的值是通过反射进行创建的,无法获取值或对值进行修改。那么我们可以通过反射的方式去获取或者修改它。
关于怎么通过反射的方式创建 struct 请参考我的另外一篇文档 go 通过字符串访问结构体

获取值

接下来演示怎么获取 field 的值

package main

import (
    "fmt"
    "reflect"
)

// Test 测试用的结构
type Test struct {
    Name string
}

func main() {
    var t interface{}= Test{Name: "niconiconi"}

    refValue := reflect.ValueOf(t) // 获取反射值
    field := refValue.FieldByName("Name") // 获取field

    name := field.Interface() // 获取 reflect.Value 的当前值


    // 可以压缩为一句
    // name := reflect.Indirect(reflect.ValueOf(t)).FieldByName("Name").Interface()

    fmt.Println(name.(string)/*可以断言回 field 的类型 具体怎么断言就看你的使用情况了*/)
}

以上是基本逻辑我们可以简单的封装一下方便我们之后的使用

package main

import (
    "fmt"
    "reflect"
)

// Test 测试用的结构
type Test struct {
    Name string
}

func main() {
    var t interface{} = &Test{Name: "niconiconi"}

    fmt.Println(GetStructFieldValue(t,"Name"))
}

// GetStructFieldValue 获取 struct 的值(不能获取私有字段)
func GetStructFieldValue(obj interface{}, fieldName string) interface{} {
    refValue := reflect.Indirect(reflect.ValueOf(obj)) // 获取可用的反射值
    if refValue.Kind() != reflect.Struct{ // 不是 struct 不管
        return nil
    }

    field := refValue.FieldByName(fieldName)
    if !field.IsValid() { // 检查是否代表值
        return nil
    }
    if !field.CanInterface() { // 检测是否可以转成 interface
        return nil
    }

    // 将当前值转化为 interface
    return field.Interface()
}

设置值

下面是如何通过反射设置 struct field 的值

package main

import (
    "fmt"
    "reflect"
)

// Test 测试用的结构
type Test struct {
    Name string
}

func main() {
    var t interface{} = &Test{Name: "niconiconi"}

    refValue := reflect.ValueOf(t)        // 反射值
    refValue = refValue.Elem()            // 取元素
    field := refValue.FieldByName("Name") // 获取 field
    field.SetString("foobar")             // 设置 新值

    fmt.Println(t) // 打印结果
}

为了方便使用简单的封装一下

package main

import (
    "errors"
    "fmt"
    "reflect"
)

// Test 测试用的结构
type Test struct {
    Name string
}

func main() {
    var t interface{} = &Test{Name: "niconiconi"}

    if err := SetStructFieldValue(t, "Name", "foobar");err != nil{
        panic(err)
    }

    fmt.Println(t) // 打印结果
}

// SetStructFieldValue 反射修改 struct field(不能修改私有字段)
func SetStructFieldValue(obj interface{}, fieldName string, value interface{}) error {
    refValue := reflect.Indirect(reflect.ValueOf(obj)) // 获取可修改的反射值

    if refValue.Kind() != reflect.Struct { // 检测是否是 struct
        return errors.New("input is not *struct")
    }
    field := refValue.FieldByName(fieldName)
    if !field.IsValid() { // 检测是否是值
        return errors.New("field does not exist")
    }

    if !field.CanSet() { // 是否可修改
        return errors.New("value cannot be changed")
    }
    refFieldValue := reflect.ValueOf(value)   // 获取需要设置的值的反射类型
    if refFieldValue.Kind() != field.Kind() { // 比对设置的值是否是原始类型
        return errors.New("value is not of type field")
    }

    // 设置值
    field.Set(refFieldValue)
    return nil
}

参考

https://stackoverflow.com/questions/24337145/get-name-of-struct-field-using-reflection
https://stackoverflow.com/questions/6395076/using-reflect-how-do-you-set-the-value-of-a-struct-field