Maison  >  Article  >  développement back-end  >  Utilisez la réflexion pour parcourir une structure de manière récursive et définir des champs

Utilisez la réflexion pour parcourir une structure de manière récursive et définir des champs

WBOY
WBOYavant
2024-02-10 20:45:10585parcourir

Utilisez la réflexion pour parcourir une structure de manière récursive et définir des champs

Dans cet article, l'éditeur PHP Xigua présentera comment utiliser la réflexion pour itérer la structure de manière récursive et définir les champs. La réflexion est une fonctionnalité puissante de PHP qui nous permet d'obtenir et de manipuler des informations telles que des classes, des méthodes, des propriétés, etc. au moment de l'exécution. La structure itérative récursive est une méthode de traitement courante, qui peut nous aider à parcourir et à exploiter des structures de données complexes. En combinant des structures de réflexion et d'itération récursive, nous pouvons facilement obtenir et définir la valeur d'un champ, permettant une programmation plus flexible et plus efficace. Dans le contenu suivant, nous présenterons ce processus en détail pour aider les lecteurs à mieux comprendre et appliquer cette technique.

Contenu de la question

Je souhaite construire un programme qui utilise la réflexion pour définir les champs d'une structure. Je l'ai fait fonctionner pour les champs de niveau supérieur, mais j'ai du mal avec les champs de structure imbriqués. Comment parcourir des champs de structure imbriqués ?

type Payload struct {
    Type    string   `json:"type"`
    SubItem *SubItem `json:"sub_item"`
}

type SubItem struct {
    Foo string `json:"foo"`
}

func main() {
    var payload Payload
    setValue(&payload, "type", "test1")
    setValue(&payload, "sub_item.foo", "test2")
}

func setValue(structPtr interface{}, key string, value string) {
    structValue := reflect.Indirect(reflect.ValueOf(structPtr))
    for i, subkey := range strings.Split(key, ".") {
        isLast := i == len(strings.Split(key, "."))-1
        var found bool
        // this line is crashing with "reflect: call of reflect.Value.NumField on zero Value"
        for i := 0; i < structValue.NumField(); i++ {
            field := structValue.Type().Field(i)
            jsonTags := strings.Split(field.Tag.Get("json"), ",")
            if jsonTags[0] == subkey {
                found = true
                if isLast {
                    if isLast {
                        // last element
                        // TODO set value
                        fmt.Printf("TODO set value %s to %v", value, structValue)
                        structValue = reflect.Indirect(reflect.ValueOf(structPtr))
                    }
                } else {
                    structValue = reflect.Indirect(reflect.ValueOf(structValue.Field(i).Interface()))
                }
                break
            }
        }
        if !found {
            panic(fmt.Errorf("failed to find field %s", key))
        }
    }
}

Solution de contournement

Utilisez cette fonctionnalité :

func setvalue(p interface{}, key string, value interface{}) {
    v := reflect.valueof(p)

    // loop through the names in key to find the target field.
    for _, name := range strings.split(key, ".") {

        // if the value is pointer, then
        // - allocate value if ptr is nil.
        // - indirect ptr
        for v.kind() == reflect.ptr {
            if v.isnil() {
                v.set(reflect.new(v.type().elem()))
            }
            v = v.elem()
        }

        // we expect that the value is struct. find the 
        // named field.
        v = findjsonfield(v, name)
        if !v.isvalid() {
            panic(fmt.sprintf("could not find field %s", key))
        }
    }
    
    // set the field.
    v.set(reflect.valueof(value))
}

La fonction findjsonfield trouve le champ de structure grâce à la balise json du champ :

func findJSONField(v reflect.Value, name string) reflect.Value {
    t := v.Type()
    for i := 0; i < v.NumField(); i++ {
        if tag, _, _ := strings.Cut(t.Field(i).Tag.Get("json"), ","); tag == name {
            return v.Field(i)
        }
    }
    return reflect.Value{}
}

https://www.php.cn/link/e4848ea6b69df2c66c87e2877e74726b

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer