mirror of
https://gitee.com/johng/gf
synced 2026-06-06 16:21:40 +08:00
improve gconv.Struct* functions for custom types conversion
This commit is contained in:
@ -1,10 +1,25 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/container/garray"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
)
|
||||
|
||||
type MyInt int
|
||||
|
||||
//func (i *MyInt) UnmarshalValue(interface{}) error {
|
||||
// *i = 10
|
||||
// return nil
|
||||
//}
|
||||
func main() {
|
||||
arr := garray.NewStrArray(false)
|
||||
arr.Unique()
|
||||
type User struct {
|
||||
Id MyInt
|
||||
}
|
||||
user := new(User)
|
||||
err := gconv.Struct(g.Map{
|
||||
"id": 1,
|
||||
}, user)
|
||||
fmt.Println(err)
|
||||
fmt.Println(user)
|
||||
}
|
||||
|
||||
@ -681,6 +681,28 @@ func Test_Model_Struct(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_Struct_CustomType(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
type MyInt int
|
||||
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id MyInt
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
err := db.Table(table).Where("id=1").Struct(user)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(user.NickName, "name_1")
|
||||
gtest.Assert(user.CreateTime.String(), "2018-10-24 10:00:00")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_Structs(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
@ -48,7 +48,8 @@ var (
|
||||
)
|
||||
|
||||
// Convert converts the variable <i> to the type <t>, the type <t> is specified by string.
|
||||
// The optional parameter <params> is used for additional parameter passing.
|
||||
// The optional parameter <params> is used for additional necessary parameter for this conversion.
|
||||
// It supports common types conversion as its conversion based on type name string.
|
||||
func Convert(i interface{}, t string, params ...interface{}) interface{} {
|
||||
switch t {
|
||||
case "int":
|
||||
@ -121,6 +122,7 @@ func Convert(i interface{}, t string, params ...interface{}) interface{} {
|
||||
case "Duration", "time.Duration":
|
||||
return Duration(i)
|
||||
default:
|
||||
|
||||
return i
|
||||
}
|
||||
}
|
||||
|
||||
@ -242,6 +242,7 @@ func bindVarToStructByIndex(elem reflect.Value, index int, value interface{}) (e
|
||||
if !structFieldValue.CanSet() {
|
||||
return nil
|
||||
}
|
||||
// If any panic, it secondly uses reflect conversion and assignment.
|
||||
defer func() {
|
||||
if recover() != nil {
|
||||
err = bindVarToReflectValue(structFieldValue, value)
|
||||
@ -250,6 +251,7 @@ func bindVarToStructByIndex(elem reflect.Value, index int, value interface{}) (e
|
||||
if empty.IsNil(value) {
|
||||
structFieldValue.Set(reflect.Zero(structFieldValue.Type()))
|
||||
} else {
|
||||
// It firstly simply assigns the value to the attribute.
|
||||
structFieldValue.Set(reflect.ValueOf(Convert(value, structFieldValue.Type().String())))
|
||||
}
|
||||
return nil
|
||||
@ -260,7 +262,8 @@ func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}) (e
|
||||
switch structFieldValue.Kind() {
|
||||
case reflect.Struct:
|
||||
if err := Struct(value, structFieldValue); err != nil {
|
||||
structFieldValue.Set(reflect.ValueOf(value))
|
||||
// Note there's reflect conversion mechanism here.
|
||||
structFieldValue.Set(reflect.ValueOf(value).Convert(structFieldValue.Type()))
|
||||
}
|
||||
// Note that the slice element might be type of struct,
|
||||
// so it uses Struct function doing the converting internally.
|
||||
@ -275,13 +278,15 @@ func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}) (e
|
||||
if t.Kind() == reflect.Ptr {
|
||||
e := reflect.New(t.Elem()).Elem()
|
||||
if err := Struct(v.Index(i).Interface(), e); err != nil {
|
||||
e.Set(reflect.ValueOf(v.Index(i).Interface()))
|
||||
// Note there's reflect conversion mechanism here.
|
||||
e.Set(reflect.ValueOf(v.Index(i).Interface()).Convert(t))
|
||||
}
|
||||
a.Index(i).Set(e.Addr())
|
||||
} else {
|
||||
e := reflect.New(t).Elem()
|
||||
if err := Struct(v.Index(i).Interface(), e); err != nil {
|
||||
e.Set(reflect.ValueOf(v.Index(i).Interface()))
|
||||
// Note there's reflect conversion mechanism here.
|
||||
e.Set(reflect.ValueOf(v.Index(i).Interface()).Convert(t))
|
||||
}
|
||||
a.Index(i).Set(e)
|
||||
}
|
||||
@ -293,13 +298,15 @@ func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}) (e
|
||||
if t.Kind() == reflect.Ptr {
|
||||
e := reflect.New(t.Elem()).Elem()
|
||||
if err := Struct(value, e); err != nil {
|
||||
e.Set(reflect.ValueOf(value))
|
||||
// Note there's reflect conversion mechanism here.
|
||||
e.Set(reflect.ValueOf(value).Convert(t))
|
||||
}
|
||||
a.Index(0).Set(e.Addr())
|
||||
} else {
|
||||
e := reflect.New(t).Elem()
|
||||
if err := Struct(value, e); err != nil {
|
||||
e.Set(reflect.ValueOf(value))
|
||||
// Note there's reflect conversion mechanism here.
|
||||
e.Set(reflect.ValueOf(value).Convert(t))
|
||||
}
|
||||
a.Index(0).Set(e)
|
||||
}
|
||||
@ -311,34 +318,40 @@ func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}) (e
|
||||
// Assign value with interface Set.
|
||||
// Note that only pointer can implement interface Set.
|
||||
if v, ok := item.Interface().(apiUnmarshalValue); ok {
|
||||
v.UnmarshalValue(value)
|
||||
err = v.UnmarshalValue(value)
|
||||
structFieldValue.Set(item)
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
elem := item.Elem()
|
||||
if err = bindVarToReflectValue(elem, value); err == nil {
|
||||
structFieldValue.Set(elem.Addr())
|
||||
}
|
||||
|
||||
// It mainly and specially handles the interface of nil value.
|
||||
case reflect.Interface:
|
||||
if value == nil {
|
||||
// Specially.
|
||||
structFieldValue.Set(reflect.ValueOf((*interface{})(nil)))
|
||||
} else {
|
||||
structFieldValue.Set(reflect.ValueOf(value))
|
||||
// Note there's reflect conversion mechanism here.
|
||||
structFieldValue.Set(reflect.ValueOf(value).Convert(structFieldValue.Type()))
|
||||
}
|
||||
|
||||
default:
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
err = errors.New(
|
||||
fmt.Sprintf(`cannot convert "%d" to type "%s"`,
|
||||
fmt.Sprintf(`cannot convert value "%d" to type "%s"`,
|
||||
value,
|
||||
structFieldValue.Type().String(),
|
||||
),
|
||||
)
|
||||
}
|
||||
}()
|
||||
structFieldValue.Set(reflect.ValueOf(value))
|
||||
// It here uses reflect converting <value> to type of the attribute and assigns
|
||||
// the result value to the attribute. It might fail and panic if the usual Go
|
||||
// conversion rules do not allow conversion.
|
||||
structFieldValue.Set(reflect.ValueOf(value).Convert(structFieldValue.Type()))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -324,6 +324,36 @@ func Test_Struct_Attr_Struct_Slice_Ptr(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Struct_Attr_CustomType1(t *testing.T) {
|
||||
type MyInt int
|
||||
type User struct {
|
||||
Id MyInt
|
||||
Name string
|
||||
}
|
||||
gtest.Case(t, func() {
|
||||
user := new(User)
|
||||
err := gconv.Struct(g.Map{"id": 1, "name": "john"}, user)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(user.Id, 1)
|
||||
gtest.Assert(user.Name, "john")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Struct_Attr_CustomType2(t *testing.T) {
|
||||
type MyInt int
|
||||
type User struct {
|
||||
Id []MyInt
|
||||
Name string
|
||||
}
|
||||
gtest.Case(t, func() {
|
||||
user := new(User)
|
||||
err := gconv.Struct(g.Map{"id": g.Slice{1, 2}, "name": "john"}, user)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(user.Id, g.Slice{1, 2})
|
||||
gtest.Assert(user.Name, "john")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Struct_PrivateAttribute(t *testing.T) {
|
||||
type User struct {
|
||||
Id int
|
||||
|
||||
Reference in New Issue
Block a user