Home  >  Article  >  Backend Development  >  Use reflection to iterate over a structure recursively and set fields

Use reflection to iterate over a structure recursively and set fields

WBOY
WBOYforward
2024-02-10 20:45:10582browse

Use reflection to iterate over a structure recursively and set fields

php editor Xigua will introduce how to use reflection recursion to iterate the structure and set the fields in this article. Reflection is a powerful feature in PHP that allows us to obtain and manipulate information such as classes, methods, properties, etc. at runtime. Recursive iteration structure is a common processing method, which can help us traverse and operate complex data structures. By combining reflection and recursive iteration structures, we can easily get and set the value of a field, allowing for more flexible and efficient programming. In the following content, we will introduce this process in detail to help readers better understand and apply this technique.

Question content

I want to build a program that uses reflection to set the fields of a structure. I got it to work for top-level fields, but I'm struggling with nested struct fields. How to iterate over nested struct fields?

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

Use this function:

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

Function findjsonfield finds the structure field through the json tag of the field:

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

The above is the detailed content of Use reflection to iterate over a structure recursively and set fields. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:stackoverflow.com. If there is any infringement, please contact admin@php.cn delete