2017-04-13 10 views
0

Ich implementiere Teilupdate für eine Entität.Aktualisieren von Struct-Feld mit Reflektion

Das Unternehmen Struktur sieht aus wie

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


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

und so weiter.

So Entity struct Felder Felder rekursiv und Update iterieren Ich versuche Reflexion zu verwenden, die Benutzer zu aktualisieren erlaubt ist:

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

Aber wenn ich versuche, einen neuen Wert mit oldField.Set(newField) zuweisen, ich erhalte eine Ausnahme:

reflect: reflect.Value.Set using unaddressable value 

ich auch reflect.ValueOf(existingEntity).Field(i).Set(reflect.ValueOf(newEntity).FieldByName(value.Name)) aber bekam die gleiche Ausnahme ausprobiert habe.

Können Sie mir erklären, warum es passiert und wie Sie das beheben können?

Antwort

0

Übergeben Sie einen Zeiger auf den Wert, den Sie aktualisieren. Hier ist die Funktion einen Zeiger für existingEntity nehmen aktualisiert:

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

Verwenden Sie es wie folgt aus:

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

Run it in the playground

Dieser behandelt die spezifischen Fall in Frage. Die Lösung ist komplizierter, wenn ein tiefer Klon benötigt wird.

+0

Vielen Dank, Ihre Lösung funktioniert für diesen Fall. Allerdings habe ich festgestellt, dass das Ding im Allgemeinen komplizierter ist: https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/apimachinery/pkg/conversion/cloner.go – dds

Verwandte Themen