From 45f30c0be3420c17a3b002025e346ceb8ce8313d Mon Sep 17 00:00:00 2001 From: Vasiliy Tolstov Date: Tue, 26 Oct 2021 14:12:37 +0300 Subject: [PATCH] util/reflect: ZeroFieldByPath and SetFieldByPath Signed-off-by: Vasiliy Tolstov --- util/reflect/struct.go | 26 ++++++++++++++++++++++++-- util/reflect/struct_test.go | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/util/reflect/struct.go b/util/reflect/struct.go index 0f580be8..54b0d400 100644 --- a/util/reflect/struct.go +++ b/util/reflect/struct.go @@ -72,9 +72,10 @@ func StructFieldByTag(src interface{}, tkey string, tval string) (interface{}, e // ZeroFieldByPath clean struct field by its path func ZeroFieldByPath(src interface{}, path string) error { var err error - var val reflect.Value + val := reflect.ValueOf(src) + for _, p := range strings.Split(path, ".") { - val, err = structValueByName(reflect.ValueOf(src), p) + val, err = structValueByName(reflect.ValueOf(val), p) if err != nil { return err } @@ -93,6 +94,27 @@ func ZeroFieldByPath(src interface{}, path string) error { 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 { diff --git a/util/reflect/struct_test.go b/util/reflect/struct_test.go index 85f5019b..48476f88 100644 --- a/util/reflect/struct_test.go +++ b/util/reflect/struct_test.go @@ -8,6 +8,40 @@ import ( rutil "go.unistack.org/micro/v3/util/reflect" ) +func TestSetFieldByPath(t *testing.T) { + type NestedStr struct { + BBB string + CCC int + } + type Str1 struct { + Name []string `json:"name" codec:"flatten"` + XXX string `json:"xxx"` + Nested NestedStr + } + type Str2 struct { + XXX string `json:"xxx"` + Nested *NestedStr + 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