go - 使用反射更新结构字段

标签 go reflection variable-assignment

我正在为实体实现部分更新。

实体结构看起来像

type Entity struct {
    Id       string `readonly:"true"`
    Spec     EntitySpec
    Status   EntityState
}


type EntitySpec struct {
    Version  *string `readonly:"true"`
    Users    []*User
    Field1   *InnerStruct1
    Field2   []*InnerStruct2
}

等等。

所以我尝试使用反射递归地迭代 Entity 结构字段并更新允许用户更新的字段:

func method(existingEntity interface{}, newEntity interface{}) {
    entityType := reflect.TypeOf(existingEntity)
    logger.Debugf("type: %v", entityType)
    for i := 0; i < entityType.NumField(); i++ {
        value := entityType.Field(i)
        logger.Debugf("Name: %s", value.Name)

        tag := value.Tag
        logger.Debugf("tag: %s", tag)
        if tag.Get("readonly") == "true" {
            logger.Debugf("readonly, go to next one")
            continue
        }

        oldField := reflect.Indirect(reflect.ValueOf(existingEntity).Field(i))
        newField := reflect.Indirect(reflect.ValueOf(newEntity).FieldByName(value.Name))
        logger.Debugf("type: %v", value.Type.Kind())
        if value.Type.Kind() == reflect.Struct {
            logger.Debugf("value: %v", oldField)
            //struct, go into it
            method(oldField.Interface(), newField.Interface())
        } else {
            if oldField != newField && oldField.String() != newField.String() {
                logger.Debugf("Type of old field: %v", oldField.Type())
                logger.Debugf("Type of new field: %v", newField.Type())
                logger.Debugf("Update: %v \nTo %v", oldField, newField)
                oldField.Set(newField) //HERE I get the exception
            } else {
                logger.Debugf("Field values are equal")
            }
        }
    }
}

但是,当我尝试使用 oldField.Set(newField) 分配一个新值时,出现异常:

reflect: reflect.Value.Set using unaddressable value

我也试过 reflect.ValueOf(existingEntity).Field(i).Set(reflect.ValueOf(newEntity).FieldByName(value.Name)) 但遇到了同样的异常。

您能向我解释一下为什么会发生这种情况以及如何解决这个问题吗?

最佳答案

传递指向您正在更新的值的指针。这是更新后的函数以获取现有实体的指针:

func method(existingEntity interface{}, newEntity interface{}) {
  entityType := reflect.TypeOf(existingEntity).Elem()
  for i := 0; i < entityType.NumField(); i++ {
    value := entityType.Field(i)
    tag := value.Tag
    if tag.Get("readonly") == "true" {
        continue
    }
    oldField := reflect.ValueOf(existingEntity).Elem().Field(i)
    newField := reflect.ValueOf(newEntity).FieldByName(value.Name)
    if value.Type.Kind() == reflect.Struct {
        method(oldField.Addr().Interface(), newField.Interface())
    } else {
        oldField.Set(newField)
    }
  }
}

像这样使用它:

var a Example
b := Example{123, 456, Example2{789}}
method(&a, b)

Run it in the playground

这处理问题中的特定情况。如果需要深度克隆,解决方案会更加复杂。

关于go - 使用反射更新结构字段,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43397975/

相关文章:

go - 如何从 Go WebAssembly 访问 DOM 元素属性?

java - 反射-EasyMock-ClassCastException

bash - 使用 “let”分配Bash失败,使用 “/”

Java >>>= 运算符

string - 使用需要转义的字符在 golang 中创建一个字符串

go - 如何在 Goreleaser 中为每个目标设置 `-ldflags` 值?

ubuntu - 如何获取 ubuntu launchpad/bazaar 包?

go - 使用反射,如何动态创建结构 "type"?

java - 通过反射迭代数组

python - Z3 Python : ordering models and accessing their elements