英文:
Updating struct field using reflection
问题
我正在实现一个实体的部分更新。
实体结构如下:
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) //在这里我得到了异常
} 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))
,但是得到了相同的异常。
你能解释一下为什么会发生这种情况,以及如何修复吗?
英文:
I'm implementing partial update for an entity.
The entity struct looks like
type Entity struct {
Id string `readonly:"true"`
Spec EntitySpec
Status EntityState
}
type EntitySpec struct {
Version *string `readonly:"true"`
Users []*User
Field1 *InnerStruct1
Field2 []*InnerStruct2
}
and so on.
So I'm trying to use reflection to iterate over Entity
struct fields recursively and update fields which user is allowed to update:
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")
}
}
}
}
But when I'm trying to assign a new value with oldField.Set(newField)
, I get an exception:
reflect: reflect.Value.Set using unaddressable value
I've also tried reflect.ValueOf(existingEntity).Field(i).Set(reflect.ValueOf(newEntity).FieldByName(value.Name))
but got the same exception.
Could you explain to me why it happens and how to fix this?
答案1
得分: 1
将要翻译的内容翻译为中文如下:
将指向要更新的值的指针传递给函数。下面是更新后的函数,用于接受现有实体的指针:
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)
这个函数处理了问题中的特定情况。如果需要进行深度克隆,则解决方案会更加复杂。
英文:
Pass a pointer to the value you are updating. Here's the function updated to take a pointer for existingEntity:
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)
}
}
}
Use it like this:
var a Example
b := Example{123, 456, Example2{789}}
method(&a, b)
This handles the specific case in the question. The solution is more complicated if a deep clone is required.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论