Compare commits
3 Commits
Author | SHA1 | Date | |
---|---|---|---|
07d4085201 | |||
45f30c0be3 | |||
bcaea675a7 |
@@ -69,6 +69,89 @@ func StructFieldByTag(src interface{}, tkey string, tval string) (interface{}, e
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
|
||||
// ZeroFieldByPath clean struct field by its path
|
||||
func ZeroFieldByPath(src interface{}, path string) error {
|
||||
var err error
|
||||
val := reflect.ValueOf(src)
|
||||
|
||||
for _, p := range strings.Split(path, ".") {
|
||||
val, err = structValueByName(val, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if IsEmpty(val) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !val.CanSet() {
|
||||
return ErrInvalidStruct
|
||||
}
|
||||
|
||||
val.Set(reflect.Zero(val.Type()))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetFieldByPath set struct field by its path
|
||||
func SetFieldByPath(src interface{}, dst interface{}, path string) error {
|
||||
var err error
|
||||
val := reflect.ValueOf(src)
|
||||
|
||||
for _, p := range strings.Split(path, ".") {
|
||||
val, err = structValueByName(val, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if !val.CanSet() {
|
||||
return ErrInvalidStruct
|
||||
}
|
||||
|
||||
val.Set(reflect.ValueOf(dst))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// structValueByName get struct field by its name
|
||||
func structValueByName(sv reflect.Value, tkey string) (reflect.Value, error) {
|
||||
if sv.Kind() == reflect.Ptr {
|
||||
sv = sv.Elem()
|
||||
}
|
||||
if sv.Kind() != reflect.Struct {
|
||||
return reflect.Zero(reflect.TypeOf(sv)), ErrInvalidStruct
|
||||
}
|
||||
|
||||
typ := sv.Type()
|
||||
for idx := 0; idx < typ.NumField(); idx++ {
|
||||
fld := typ.Field(idx)
|
||||
val := sv.Field(idx)
|
||||
if len(fld.PkgPath) != 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if fld.Name == tkey || strings.EqualFold(strings.ToLower(fld.Name), strings.ToLower(tkey)) {
|
||||
return val, nil
|
||||
}
|
||||
|
||||
switch val.Kind() {
|
||||
case reflect.Ptr:
|
||||
if val = val.Elem(); val.Kind() == reflect.Struct {
|
||||
if iface, err := structValueByName(val, tkey); err == nil {
|
||||
return iface, nil
|
||||
}
|
||||
}
|
||||
case reflect.Struct:
|
||||
if iface, err := structValueByName(val, tkey); err == nil {
|
||||
return iface, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return reflect.Zero(reflect.TypeOf(sv)), ErrNotFound
|
||||
}
|
||||
|
||||
// StructFieldByPath get struct field by its path
|
||||
func StructFieldByPath(src interface{}, path string) (interface{}, error) {
|
||||
var err error
|
||||
@@ -98,10 +181,7 @@ func StructFieldByName(src interface{}, tkey string) (interface{}, error) {
|
||||
if len(fld.PkgPath) != 0 {
|
||||
continue
|
||||
}
|
||||
if fld.Name == tkey {
|
||||
if val.Kind() != reflect.Ptr && val.CanAddr() {
|
||||
val = val.Addr()
|
||||
}
|
||||
if fld.Name == tkey || strings.EqualFold(strings.ToLower(fld.Name), strings.ToLower(tkey)) {
|
||||
return val.Interface(), nil
|
||||
}
|
||||
|
||||
|
@@ -8,6 +8,80 @@ import (
|
||||
rutil "go.unistack.org/micro/v3/util/reflect"
|
||||
)
|
||||
|
||||
func TestSetFieldByPath(t *testing.T) {
|
||||
type NestedStr struct {
|
||||
BBB string `json:"bbb"`
|
||||
CCC int `json:"ccc"`
|
||||
}
|
||||
type Str1 struct {
|
||||
Name []string `json:"name" codec:"flatten"`
|
||||
XXX string `json:"xxx"`
|
||||
Nested NestedStr `json:"nested"`
|
||||
}
|
||||
type Str2 struct {
|
||||
XXX string `json:"xxx"`
|
||||
Nested *NestedStr `json:"nested"`
|
||||
Name []string `json:"name" codec:"flatten"`
|
||||
}
|
||||
var err error
|
||||
val1 := &Str1{Name: []string{"first", "second"}, XXX: "ttt", Nested: NestedStr{BBB: "ddd", CCC: 9}}
|
||||
val2 := &Str2{Name: []string{"first", "second"}, XXX: "ttt", Nested: &NestedStr{BBB: "ddd", CCC: 9}}
|
||||
err = rutil.SetFieldByPath(val1, "xxx", "Nested.BBB")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if val1.Nested.BBB != "xxx" {
|
||||
t.Fatalf("SetFieldByPath not works: %#+v", val1)
|
||||
}
|
||||
err = rutil.SetFieldByPath(val2, "xxx", "Nested.BBB")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if val2.Nested.BBB != "xxx" {
|
||||
t.Fatalf("SetFieldByPath not works: %#+v", val1)
|
||||
}
|
||||
}
|
||||
|
||||
func TestZeroFieldByPath(t *testing.T) {
|
||||
type NestedStr struct {
|
||||
BBB string `json:"bbb"`
|
||||
CCC int `json:"ccc"`
|
||||
}
|
||||
type Str1 struct {
|
||||
Name []string `json:"name" codec:"flatten"`
|
||||
XXX string `json:"xxx"`
|
||||
Nested NestedStr `json:"nested"`
|
||||
}
|
||||
type Str2 struct {
|
||||
XXX string `json:"xxx"`
|
||||
Nested *NestedStr `json:"nested"`
|
||||
Name []string `json:"name" codec:"flatten"`
|
||||
}
|
||||
var err error
|
||||
val1 := &Str1{Name: []string{"first", "second"}, XXX: "ttt", Nested: NestedStr{BBB: "ddd", CCC: 9}}
|
||||
|
||||
err = rutil.ZeroFieldByPath(val1, "Nested.BBB")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = rutil.ZeroFieldByPath(val1, "Nested")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if val1.Nested.BBB == "ddd" {
|
||||
t.Fatalf("zero field not works: %v", val1)
|
||||
}
|
||||
|
||||
val2 := &Str2{Name: []string{"first", "second"}, XXX: "ttt", Nested: &NestedStr{BBB: "ddd", CCC: 9}}
|
||||
err = rutil.ZeroFieldByPath(val2, "Nested")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if val2.Nested != nil {
|
||||
t.Fatalf("zero field not works: %v", val2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStructFieldsMap(t *testing.T) {
|
||||
type NestedStr struct {
|
||||
BBB string
|
||||
@@ -108,9 +182,9 @@ func TestStructByName(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if v, ok := iface.(*[]string); !ok {
|
||||
t.Fatalf("not *[]string %v", iface)
|
||||
} else if len(*v) != 2 {
|
||||
if v, ok := iface.([]string); !ok {
|
||||
t.Fatalf("not []string %v", iface)
|
||||
} else if len(v) != 2 {
|
||||
t.Fatalf("invalid number %v", iface)
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user