diff --git a/util/gconv/gconv_convert.go b/util/gconv/gconv_convert.go index baf48b48d..a5c6e99fc 100644 --- a/util/gconv/gconv_convert.go +++ b/util/gconv/gconv_convert.go @@ -254,22 +254,50 @@ func doConvert(in doConvertInput) (convertedValue interface{}) { default: if in.ReferValue != nil { - var referReflectValue reflect.Value + var ( + referReflectValue reflect.Value + ) if v, ok := in.ReferValue.(reflect.Value); ok { referReflectValue = v } else { referReflectValue = reflect.ValueOf(in.ReferValue) } + defer func() { if recover() != nil { + in.alreadySetToReferValue = false if err := bindVarToReflectValue(referReflectValue, in.FromValue, nil); err == nil { in.alreadySetToReferValue = true convertedValue = referReflectValue.Interface() } } }() + if referReflectValue.Kind() == reflect.Ptr { + // Type converting for custom type pointers. + // Eg: + // type PayMode int + // type Req struct{ + // Mode *PayMode + // } + // + // Struct(`{"Mode": 1000}`, &req) + originType := referReflectValue.Type().Elem() + switch originType.Kind() { + case reflect.Struct: + // Not support some kinds. + default: + in.ToTypeName = originType.Kind().String() + in.ReferValue = nil + refElementValue := reflect.ValueOf(doConvert(in)) + originTypeValue := reflect.New(refElementValue.Type()).Elem() + originTypeValue.Set(refElementValue) + in.alreadySetToReferValue = true + return originTypeValue.Addr().Convert(referReflectValue.Type()).Interface() + } + } in.ToTypeName = referReflectValue.Kind().String() in.ReferValue = nil + in.alreadySetToReferValue = true return reflect.ValueOf(doConvert(in)).Convert(referReflectValue.Type()).Interface() } return in.FromValue diff --git a/util/gconv/gconv_z_unit_struct_test.go b/util/gconv/gconv_z_unit_struct_test.go index 629c15bf8..156b66c44 100644 --- a/util/gconv/gconv_z_unit_struct_test.go +++ b/util/gconv/gconv_z_unit_struct_test.go @@ -1321,3 +1321,31 @@ func Test_Scan_WithDoubleSliceAttribute(t *testing.T) { }) } + +func Test_Struct_WithCustomType(t *testing.T) { + type PayMode int + + type Req1 struct { + PayMode PayMode + } + type Req2 struct { + PayMode *PayMode + } + var ( + params = gconv.Map(`{"PayMode": 1000}`) + req1 *Req1 + req2 *Req2 + err1 error + err2 error + ) + err1 = gconv.Struct(params, &req1) + err2 = gconv.Struct(params, &req2) + gtest.C(t, func(t *gtest.T) { + t.AssertNil(err1) + t.Assert(req1.PayMode, 1000) + + t.AssertNil(err2) + t.AssertNE(req2.PayMode, nil) + t.Assert(*req2.PayMode, 1000) + }) +}