2017-04-13 1 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: reflect.Value.Set using unaddressable value 

私もreflect.ValueOf(existingEntity).Field(i).Set(reflect.ValueOf(newEntity).FieldByName(value.Name))を試してみましたが、同じ例外を持っている:私は例外を取得します。

なぜそれが起こり、これを修正する方法を私に説明できますか?

答えて

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

関連する問題