web-dev-qa-db-fra.com

Comment obtenir une valeur sous-jacente à partir d'une valeur Reflect dans Golang?

J'ai donc trouvé du code qui m'aide à démarrer la réflexion dans Go (golang), mais j'ai du mal à obtenir une valeur sous-jacente pour pouvoir créer un map[string]string à partir d'une structure et de ses champs.

Finalement, je voudrais faire du résultat un map[string]interface{}, mais ce seul problème me bloque en quelque sorte.

Le code que j'ai en ce moment:

package main

import (
    "fmt"
    "reflect"
)

type Foo struct {
    FirstName string `tag_name:"tag 1"`
    LastName  string `tag_name:"tag 2"`
    Age       int  `tag_name:"tag 3"`
}

func inspect(f interface{}) map[string]string {

    m := make(map[string]string)
    val := reflect.ValueOf(f).Elem()

    for i := 0; i < val.NumField(); i++ {
        valueField := val.Field(i)
        typeField := val.Type().Field(i)

        f := valueField.Interface()
        val := reflect.ValueOf(f)
        m[typeField.Name] = val.String()
    }

    return m
}

func dump(m map[string]string) {

    for k, v := range m {
        fmt.Printf("%s : %s\n", k, v)
    }
}

func main() {
    f := &Foo{
        FirstName: "Drew",
        LastName:  "Olson",
        Age:       30,
    }

    a := inspect(f)

    dump(a)
}

Le résultat de l'exécution du code:

FirstName : Drew
LastName : Olson
Age : <int Value>

D'après ce que je comprends, la sortie pour FirstName et LastName sont des objets reflet.Value réels, mais pour les chaînes, la méthode String () sur la valeur ne produit que la chaîne sous-jacente. J'aimerais obtenir l'int et le changer en une chaîne, mais à partir de la documentation du paquet relect, je ne vois pas immédiatement comment cela se fait.

Soo .... Comment puis-je obtenir la valeur sous-jacente d'un reflet.Value dans golang?

30
lucidquiet

Un bon exemple de la façon d'analyser les valeurs est le package fmt. Voir ce code .

L'utilisation du code mentionné pour correspondre à votre problème ressemblerait à ceci:

switch val.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
    m[typeField.Name] = strconv.FormatInt(val.Int(), 10)
case reflect.String:
    m[typeField.Name] = val.String()    
// etc...
}

Fondamentalement, vous devez vérifier tous types disponibles .

14
nemo

Il semble que vous soyez sur la bonne voie. Le problème que je vois avec votre code est qu'il fait des hypothèses sur les valeurs, ce qui signifie quand appelez-vous Elem() et combien de fois (pour résoudre les pointeurs). Pour le savoir, vous devez regarder le reflect.Kind. La valeur est-elle un reflect.Ptr? Utilisez ensuite Elem().

Une fois que vous avez la valeur de val.Interface()/val.String()/val.Int(), vous pouvez convertir vos valeurs selon vos besoins. Ce que vous utiliserez dépendra de reflect.Kind. Pour convertir un int vers/depuis string, vous devez utiliser le package strconv.

Les packages encoding/json Et encoding/xml Font déjà ce genre de travail. Le code source fournit de bons exemples. Par exemple, jetez un œil à copyValue in encoding/xml/read.go et marshalSimple in encoding/xml/marshal.go .

4
Luke

Cela devrait être plus facile à faire avec Go 1.5 (août 2015) Voir revue 8731 et commit 049b89d par Rob Pike (robpike) =:

fmt: traite reflect.Value spécialement - comme la valeur qu'il contient

Cela vous permettrait d'imprimer la valeur réelle d'un argument Reflect.Value():

Lorsqu'un reflect.Value Est passé à Printf (etc.), fmt appelle la méthode String, qui ne divulgue pas son contenu.
Pour obtenir le contenu, on pourrait appeler Value.Interface(), mais c'est illégal si le Value n'est pas exporté ou autrement interdit.

Ce CL améliore la situation avec une modification triviale du package fmt: lorsque nous voyons un reflect.Value Comme argument, nous le traitons exactement comme nous traitons un reflect.Value Que nous faisons à l'intérieur le paquet.
Cela signifie que nous imprimons toujours le contenu de Value comme si qui était l'argument de Printf.

C'est sans doute un changement de rupture, mais je pense que c'est une véritable amélioration et pas plus de rupture que de nombreux autres ajustements que nous avons apportés à la sortie formatée de ce package.

2
VonC