2017-04-13 1 views
0

Je suis en train d'implémenter une mise à jour partielle pour une entité.Mise à jour du champ structure à l'aide de la réflexion

La struct entité ressemble

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


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

et ainsi de suite.

Je suis en train d'utiliser la réflexion pour itérer sur Entity champs de struct récursive et les champs de mise à jour qui l'utilisateur est autorisé à mettre à jour:

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") 
      } 
     } 
    } 
} 

Mais quand je suis en train d'attribuer une nouvelle valeur avec oldField.Set(newField), Je reçois une exception:

reflect: reflect.Value.Set using unaddressable value 

J'ai aussi essayé reflect.ValueOf(existingEntity).Field(i).Set(reflect.ValueOf(newEntity).FieldByName(value.Name)) mais ai la même exception.

Pourriez-vous m'expliquer pourquoi cela se produit et comment y remédier?

Répondre

0

Passez un pointeur sur la valeur que vous mettez à jour. Voici la fonction mis à jour pour prendre un pointeur pour 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) 
    } 
    } 
} 

utiliser comme ceci:

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

Run it in the playground

Ce traite le cas spécifique de la question. La solution est plus compliquée si un clone profond est requis.

+0

Merci, votre solution fonctionne pour ce cas. Cependant, j'ai découvert que cette chose est plus compliquée dans le cas général: https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/apimachinery/pkg/conversion/cloner.go – dds