Go 使用反射更新结构字段
我正在为一个实体实现部分更新 实体结构看起来像Go 使用反射更新结构字段,go,reflection,variable-assignment,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
type Entity struct {
Id string `readonly:"true"`
Spec EntitySpec
Status EntityState
}
type EntitySpec struct {
Version *string `readonly:"true"`
Users []*User
Field1 *InnerStruct1
Field2 []*InnerStruct2
}
等等
因此,我尝试使用反射递归地迭代实体
结构字段,并更新允许用户更新的字段:
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")
}
}
}
}
我还尝试了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)
}
}
}
这将处理问题中的具体情况。如果需要深度克隆,解决方案会更复杂。谢谢,您的解决方案适用于这种情况。然而,我发现一般情况下事情更复杂:
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)