diff --git a/util/gconv/gconv_struct.go b/util/gconv/gconv_struct.go index 5d48e015a..ac302b8fc 100644 --- a/util/gconv/gconv_struct.go +++ b/util/gconv/gconv_struct.go @@ -57,6 +57,13 @@ func Struct(params interface{}, pointer interface{}, mapping ...map[string]strin return fmt.Errorf("invalid params: %v", params) } + // UnmarshalValue. + // Assign value with interface UnmarshalValue. + // Note that only pointer can implement interface UnmarshalValue. + if v, ok := pointer.(apiUnmarshalValue); ok { + return v.UnmarshalValue(params) + } + // Using reflect to do the converting, // it also supports type of reflect.Value for (always in internal usage). elem, ok := pointer.(reflect.Value) @@ -71,6 +78,7 @@ func Struct(params interface{}, pointer interface{}, mapping ...map[string]strin } elem = rv.Elem() } + // It automatically creates struct object if necessary. // For example, if is **User, then is *User, which is a pointer to User. if elem.Kind() == reflect.Ptr { @@ -79,14 +87,19 @@ func Struct(params interface{}, pointer interface{}, mapping ...map[string]strin elem.Set(e.Addr()) elem = e } else { - // Assign value with interface Set. - // Note that only pointer can implement interface Set. - if v, ok := elem.Interface().(apiUnmarshalValue); ok { - v.UnmarshalValue(params) - return nil - } + elem = elem.Elem() } } + + // UnmarshalValue. + // Assign value with interface UnmarshalValue. + // Note that only pointer can implement interface UnmarshalValue. + if elem.Kind() == reflect.Struct { + if v, ok := elem.Addr().Interface().(apiUnmarshalValue); ok { + return v.UnmarshalValue(params) + } + } + // It only performs one converting to the same attribute. // doneMap is used to check repeated converting, its key is the real attribute name // of the struct. diff --git a/util/gconv/gconv_z_unit_struct_test.go b/util/gconv/gconv_z_unit_struct_test.go index 956259db5..2c43e7b68 100644 --- a/util/gconv/gconv_z_unit_struct_test.go +++ b/util/gconv/gconv_z_unit_struct_test.go @@ -805,3 +805,35 @@ func Test_Struct_CatchPanic(t *testing.T) { t.AssertNE(err, nil) }) } + +type MyTime struct { + time.Time +} + +type MyTimeSt struct { + ServiceDate MyTime +} + +func (st *MyTimeSt) UnmarshalValue(v interface{}) error { + m := gconv.Map(v) + t, err := gtime.StrToTime(gconv.String(m["ServiceDate"])) + if err != nil { + return err + } + st.ServiceDate = MyTime{t.Time} + return nil +} + +func Test_Struct_UnmarshalValue(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + st := &MyTimeSt{} + err := gconv.Struct(g.Map{"ServiceDate": "2020-10-10 12:00:01"}, st) + t.Assert(err, nil) + t.Assert(st.ServiceDate.Time.Format("2006-01-02 15:04:05"), "2020-10-10 12:00:01") + }) + gtest.C(t, func(t *gtest.T) { + st := &MyTimeSt{} + err := gconv.Struct(g.Map{"ServiceDate": nil}, st) + t.AssertNE(err, nil) + }) +}