【问题标题】:how to update a map value by reflection in Golang如何通过 Golang 中的反射更新地图值
【发布时间】:2021-07-05 00:08:28
【问题描述】:

我遇到了一个问题,我希望做一个通用的方法来更新map中的值,如下例:

我主要想做的,提供一个通用的方法

  1. 读取配置;
  2. 验证配置是否有效
  3. 将配置反序列化为指定对象
type ConfigKey string

var StudentKey ConfigKey
var TeacherKey ConfigKey

type Teacher struct {
    Age int `json:"age"`
}

func TestReflect(t *testing.T) {
    // update student
    student := make(map[int]Student)
    update(StudentKey, &student, func(value interface{}) bool {
        if m, ok := value.(map[int]Student); ok && m[0].Name == "test" {
            return true
        }
        return false
    })
    fmt.Println("outer:", student)
    // update teacher
    teacher := make(map[int]Teacher)
    update(TeacherKey, &teacher, func(value interface{}) bool {
        if m, ok := value.(map[int]Teacher); ok && m[0].Age == 1 {
            return true
        }
        return false
    })
    fmt.Println("outer:", teacher)
}

// func to auto update config, For make a general method
func update(key ConfigKey, ori interface{}, validate func(value interface{}) bool) {
    valueStr := getConfigValue(key)
    temp := copyInterface(ori)
    _ = json.Unmarshal([]byte(valueStr), &temp)
    if !validate(valueStr) {
        return
    }
    fmt.Println("inner:", temp)
    ori = temp
}

func getConfigValue(key ConfigKey) string {
    switch key {
    case StudentKey:
        return `{"0":{"name":"test"}}`
    case TeacherKey:
        return `{"0":{"age":1}}`
    }
    return ""
}

func copyInterface(a interface{}) interface{} {
    return reflect.New(reflect.TypeOf(a).Elem())
}

预期的输出结果是:

inner: map[0:map[name:test]]
outer: map[0:map[name:test]]
inner: map[0:map[age:1]]
outer: map[0:map[age:1]]

但实际输出是:

outer: map[]
outer: map[]

我怎样才能达到预期的输出?

【问题讨论】:

  • 您可以链接到其他事物以供参考,但您的问题应该是自包含的(理解问题不需要链接)。此外,在没有提出自己尝试的情况下提出问题是一种被否决且没有答案的好方法。
  • 调试你的代码。例如:在您的验证函数中添加fmt.Print... 指令。您可以使用"%T" 打印任何变量的类型:fmt.Printf("type of x: %T\n", x)

标签: go reflection


【解决方案1】:

这是更正后的更新函数,并附有描述函数工作原理的注释。

func update(key ConfigKey, ptarget interface{}, validate func(interface{}) bool) {
    valueStr := getConfigValue(key)

    // Get target value. Target is expected to be a pointer to
    // a value. Elem() dereferences the pointer.
    target := reflect.ValueOf(ptarget).Elem()

    // Create a new empty value. New returns a pointer. Elem()
    // dereferences the pointer.
    scratch := reflect.New(target.Type()).Elem()

    // Unmarshal to the scratch value.
    err := json.Unmarshal([]byte(valueStr), scratch.Addr().Interface())
    if err != nil {
        return
    }

    // Call the validation function.
    if !validate(scratch.Interface()) {
        return
    }

    // Assign the scratch value to the target value.
    target.Set(scratch)
}

Run the test on the Go Playground

【讨论】:

  • 非常感谢,已经成功了。看来我对反射的了解还是太少了。
猜你喜欢
  • 2020-10-30
  • 1970-01-01
  • 2017-10-11
  • 1970-01-01
  • 2020-02-02
  • 2016-12-05
  • 2013-06-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多