在日常开中,总是会有各种trick的需求,Go这种强类型语言按常规写法就不能完成一些需求,所以反射就是一般瑞士军刀,在某些场合可以用简单的方法功能。
每当这个时候,就无比怀恋Python
下面列出一些通过反射操作结构体的常用方法
package main
import (
"encoding/json"
"errors"
"fmt"
"reflect"
)
type User struct {
Name string
Age int
}
func (u *User) String() string {
bs, _ := json.Marshal(u)
return string(bs)
}
func SetStructValueString(obj *User, field string, value string) error {
rv := reflect.ValueOf(obj)
if rv.Kind() == reflect.Ptr {
rv = rv.Elem()
}
if !rv.CanSet() {
return errors.New("obj cannot set")
}
f := rv.FieldByName(field)
if !f.IsValid() {
return errors.New("field " + field + " not exists")
}
f.SetString(value)
return nil
}
说明:
func SetStructValueStringPtr(obj *User, field string, value string) error {
rv := reflect.ValueOf(obj)
if rv.Kind() == reflect.Ptr {
rv = rv.Elem()
}
if !rv.CanSet() {
return errors.New("obj cannot set")
}
f := rv.FieldByName(field)
if !f.IsValid() {
return errors.New("field " + field + " not exists")
}
f.Set(reflect.ValueOf(&value))
return nil
}
说明:
package main
import (
"encoding/json"
"errors"
"fmt"
"reflect"
)
type User struct {
Name string
Age int
Email *string
Siblings []string
}
func (u *User) String() string {
bs, _ := json.Marshal(u)
return string(bs)
}
func AppendStructValueSlice(obj *User, field string, values []string) error {
rv := reflect.ValueOf(obj)
if rv.Kind() == reflect.Ptr {
rv = rv.Elem()
}
if !rv.CanSet() {
return errors.New("obj cannot set")
}
f := rv.FieldByName(field)
if !f.IsValid() {
return errors.New("field " + field + " not exists")
}
for _, v := range values {
f = reflect.Append(f, reflect.ValueOf(v))
}
// or用如下方法
//f = reflect.AppendSlice(f, reflect.ValueOf(values))
rv.FieldByName(field).Set(f)
return nil
}
func main() {
name := "Smith"
obj := &User{Name: name, Age: 18, Siblings: []string{"Van"}}
fmt.Println(obj)
AppendStructValueSlice(obj, "Siblings", []string{"John", "Kelly"})
fmt.Println(obj)
}
Output:
{"Name":"Smith","Age":18,"Email":null,"Siblings":["Van"]}
{"Name":"Smith","Age":18,"Email":null,"Siblings":["Van","John","Kelly"]}
说明:
从这个例子可以看到,对这种容器类型,获取到的field也可以认为是一个特殊的slice,然后对需要加入的元素依次进行添加,最后因为field已经被重新赋值,那么需要重新执行FieldByName获取field变量的Set方法
那么,如果我们要删除slice中的一个元素呢
也很简单,我们知道如果这个字段时slice,那么f就是可以遍历的,我们先创建一个空的slice value,然后遍历f,过滤掉要删除的值,其余值保存到我们的新的slice value中,最后把这个新的slice value设置到我们的成员上去就可以了
func DeleteStructValueSlice(obj *User, field string, values string) error {
rv := reflect.ValueOf(obj)
if rv.Kind() == reflect.Ptr {
rv = rv.Elem()
}
if !rv.CanSet() {
return errors.New("obj cannot set")
}
f := rv.FieldByName(field)
if !f.IsValid() {
return errors.New("field " + field + " not exists")
}
v := reflect.ValueOf([]string{})
for i := 0; i < f.Len(); i++ {
if f.Index(i).String() == values {
continue
}
v = reflect.Append(v, f.Index(i))
}
rv.FieldByName(field).Set(v)
return nil
}
此外,map类型的对象的修改思路都大概如此,如果有需要再补充吧
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。