Use reflection to iterate over a structure recursively and set fields

WBOY
Release: 2024-02-10 20:45:10
forward
585 people have browsed it

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))
        }
    }
}
Copy after login

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))
}
Copy after login

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{}
}
Copy after login

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!

source:stackoverflow.com
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
About us Disclaimer Sitemap
php.cn:Public welfare online PHP training,Help PHP learners grow quickly!