2017-08-23 3 views
0

Je souhaite créer une méthode qui prend une structure comme interface{} et renvoie true si l'un des champs de la structure fournie est nul.Marche récursive dans les structures imbriquées

Voici ce que j'ai en ce moment:

// ContainsNil returns true if any fields within the supplied structure are nil. 
// 
// If the supplied object is not a struct, the method will panic. 
// Nested structs are inspected recursively. 
// Maps and slices are not inspected deeply. This may change. 
func ContainsNil(obj interface{}) bool { 
    if obj == nil { 
     return true 
    } 
    s := reflect.Indirect(reflect.ValueOf(obj)) 
    for i := 0; i < s.NumField(); i++ { 
     f := s.Type().Field(i) 
     field := s.Field(i) 
     if fieldIsExported(f) { // Exported-check must be evaluated first to avoid panic. 
      if field.Kind() == reflect.Struct { 
       if ContainsNil(field.Addr()) { 
        return true 
       } 
      } else { 
       if field.IsNil() { 
        return true 
       } 
       if field.Interface() == nil { 
        return true 
       } 
      } 
     } 
    } 
    return false 
} 

func fieldIsExported(field reflect.StructField) bool { 
    log.Println(field.Name) 
    return field.Name[0] >= 65 == true && field.Name[0] <= 90 == true 
} 

Et un test défaillant:

func Test_ContainsNil_NilNestedValue_ReturnsTrue(t *testing.T) { 
    someNestedStruct := &c.SomeNestedStruct{ 
     SomeStruct: c.SomeStruct{ 
      SomeString: nil, 
     }, 
    } 
    result := util.ContainsNil(someNestedStruct) 
    assert.True(t, result) 
} 

Le code de test exécute sans paniquer, mais échoue parce que la méthode renvoie false plutôt que true. Le problème que j'ai est que je ne peux pas comprendre comment passer correctement la structure imbriquée dans l'appel récursif à ContainsNil.

Lorsque l'appel récursif est effectué pour la structure imbriquée, la méthode fieldIsExported renvoie false car elle ne reçoit pas la valeur attendue.

Je m'attends à ce que fieldIsExported reçoive "SomeStruct" lors de son premier appel et reçoive "SomeString" lors du second appel (récursif). Le premier appel se passe comme prévu, mais sur le deuxième appel, fieldIsExported reçoit "typ", quand je m'attends à recevoir "SomeString".

J'ai fait beaucoup de recherches sur l'utilisation de la réflexion sur les structures, mais je n'ai pas encore réussi à comprendre. Des idées?

Références:

Répondre

1

Vous vérifiez si le champ actuel est une struct valeur, mais vous compte jamais pour le cas où n c'est reflect.Ptr à une structure ou à quelque chose d'autre, donc votre fonction ne sera jamais récursive dans ce cas. Voici votre fonction avec la pièce manquante.

https://play.golang.org/p/FdLxeee9UU

// ContainsNil returns true if any fields within the supplied structure are nil. 
// 
// If the supplied object is not a struct, the method will panic. 
// Nested structs are inspected recursively. 
// Maps and slices are not inspected deeply. This may change. 
func ContainsNil(obj interface{}) bool { 
    if obj == nil { 
     return true 
    } 
    s := reflect.Indirect(reflect.ValueOf(obj)) 
    for i := 0; i < s.NumField(); i++ { 
     f := s.Type().Field(i) 
     field := s.Field(i) 
     if fieldIsExported(f) { // Exported-check must be evaluated first to avoid panic. 
      if field.Kind() == reflect.Ptr { // case when it's a pointer or struct pointer 
       if field.IsNil() { 
        return true 
       } 
       if ContainsNil(field.Interface()) { 
        return true 
       } 
      } 
      if field.Kind() == reflect.Struct { 
       if ContainsNil(field.Addr()) { 
        return true 
       } 
      } else { 
       if field.IsNil() { 
        return true 
       } 
       if field.Interface() == nil { 
        return true 
       } 
      } 
     } 
    } 
    return false 
} 
+0

Cela prend tout son sens. Merci beaucoup pour l'aide! – Joe