rakesh99
rakesh99

Reputation: 1266

Generically modify struct fields using reflection in golang

Below is a method which uses reflect package to modify fields of a struct ,this works for a specific struct type

func modify(obj Car) interface{} {
        ty := reflect.TypeOf(obj)
        for i := 0; i < ty.NumField(); i++ {
            rval := reflect.Indirect(reflect.ValueOf(&obj))
            field := rval.Field(i)
            fieldType := field.Kind()
    
            switch fieldType {
            case reflect.String:
                field.SetString("")
            case reflect.Int:
                field.SetInt(0)
            case reflect.Ptr:
                field.Set(reflect.ValueOf(nil))
    
            }
        }
        return obj
    }

modifying the signature to

func modify(obj interface{}) interface{} {

results in panic: reflect: call of reflect.Value.Field on interface Value

at line

 field := rval.Field(i)

https://go.dev/play/p/pGfKtIg5RUp It works with the signature

func modify(obj Car) interface{} {

https://go.dev/play/p/31Oh6WLmlGP

Why is the compile time type modifying the behaviour ? The goal here is to mask certain fields based on struct tags .It could wrap an endpoint and the input and output to the method being wrapped could be struct or pointer so in above case both calls should work

modify(car)

modify(&car)

Upvotes: 0

Views: 824

Answers (1)

rakesh99
rakesh99

Reputation: 1266

This is how it works for both value and pointer types

func modify(obj interface{}) interface{} {
    rv := reflect.ValueOf(obj)
    trv := reflect.TypeOf(obj)
    value := reflect.New(rv.Type())
    if rv.Kind() == reflect.Pointer {
        rv = reflect.ValueOf(obj).Elem()
        trv = reflect.TypeOf(obj).Elem()
        value = reflect.New(rv.Type())
    }
    for i := 0; i < rv.NumField(); i++ {
        field := rv.Field(i)
        fieldType := field.Kind()
        v := value.Elem().Field(i)
        tag, _ := trv.Field(i).Tag.Lookup("es")
        if len(tag) != 0 {

            switch fieldType {
            case reflect.String:
                v.SetString(tag)
            case reflect.Int:
                v.SetInt(0)
            case reflect.Ptr:
                v.Set(reflect.ValueOf(nil))

            }
        } else {
            v.Set(field)
        }
    }

    return value
}

https://go.dev/play/p/C1pqw_UbPcG

Upvotes: 0

Related Questions