2017-04-13 5 views
0

엔티티에 대한 부분 업데이트를 구현 중입니다.리플렉션을 사용하여 구조 필드 업데이트하기

엔티티 구조체 보이는 등

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.ValueOf(existingEntity).Field(i).Set(reflect.ValueOf(newEntity).FieldByName(value.Name))을 시도했지만 같은 예외를 가지고

reflect: reflect.Value.Set using unaddressable value 

: 나는 예외를 얻을.

왜 그런지 어떻게 해결할 수 있습니까?

답변

0

업데이트 할 값을 가리키는 포인터를 전달하십시오. 여기 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) 
    } 
    } 
} 

이처럼 사용

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

Run it in the playground

이는 문제의 특정 사건을 처리합니다. 깊은 복제가 필요한 경우 솔루션이 더 복잡합니다.

+0

감사합니다.이 경우 솔루션이 작동합니다. 그러나 일반적으로 문제가 더 복잡하다는 것을 알게되었습니다. https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/apimachinery/pkg/conversion/cloner.go – dds