diff --git a/convertor/convertor.go b/convertor/convertor.go index 967d5e7..81f6920 100644 --- a/convertor/convertor.go +++ b/convertor/convertor.go @@ -278,44 +278,99 @@ func DeepClone(src interface{}) interface{} { return result.Interface() } -// CopyProperties copies each field from the source into the destination. It recursively copies struct pointers and interfaces that contain struct pointers. -func CopyProperties(dst, src interface{}) (err error) { - defer func() { - if e := recover(); e != nil { - err = errors.New(fmt.Sprintf("%v", e)) - } - }() +// // CopyProperties copies each field from the source into the destination. It recursively copies struct pointers and interfaces that contain struct pointers. +// func CopyProperties(dst, src interface{}) (err error) { +// defer func() { +// if e := recover(); e != nil { +// err = errors.New(fmt.Sprintf("%v", e)) +// } +// }() - dstType, dstValue := reflect.TypeOf(dst), reflect.ValueOf(dst) - srcType, srcValue := reflect.TypeOf(src), reflect.ValueOf(src) +// dstType, dstValue := reflect.TypeOf(dst), reflect.ValueOf(dst) +// srcType, srcValue := reflect.TypeOf(src), reflect.ValueOf(src) + +// if dstType.Kind() != reflect.Ptr || dstType.Elem().Kind() != reflect.Struct { +// return errors.New("CopyProperties: param dst should be struct pointer") +// } + +// if srcType.Kind() == reflect.Ptr { +// srcType, srcValue = srcType.Elem(), srcValue.Elem() +// } +// if srcType.Kind() != reflect.Struct { +// return errors.New("CopyProperties: param src should be a struct or struct pointer") +// } + +// dstType, dstValue = dstType.Elem(), dstValue.Elem() + +// propertyNums := dstType.NumField() + +// for i := 0; i < propertyNums; i++ { +// property := dstType.Field(i) +// propertyValue := srcValue.FieldByName(property.Name) + +// if !propertyValue.IsValid() || property.Type != propertyValue.Type() { +// continue +// } + +// if dstValue.Field(i).CanSet() { +// dstValue.Field(i).Set(propertyValue) +// } +// } + +// return nil +// } + +// CopyProperties copies each field from the source into the destination. It recursively copies struct pointers and interfaces that contain struct pointers. +// use json.Marshal/Unmarshal, so json tag should be set for fields of dst and src struct. +func CopyProperties(dst, src interface{}) error { + dstType, srcType := reflect.TypeOf(dst), reflect.TypeOf(src) if dstType.Kind() != reflect.Ptr || dstType.Elem().Kind() != reflect.Struct { - return errors.New("CopyProperties: param dst should be struct pointer") + return errors.New("CopyProperties: parameter dst should be struct pointer") } if srcType.Kind() == reflect.Ptr { - srcType, srcValue = srcType.Elem(), srcValue.Elem() + srcType = srcType.Elem() } if srcType.Kind() != reflect.Struct { - return errors.New("CopyProperties: param src should be a struct or struct pointer") + return errors.New("CopyProperties: parameter src should be a struct or struct pointer") } - dstType, dstValue = dstType.Elem(), dstValue.Elem() - - propertyNums := dstType.NumField() - - for i := 0; i < propertyNums; i++ { - property := dstType.Field(i) - propertyValue := srcValue.FieldByName(property.Name) - - if !propertyValue.IsValid() || property.Type != propertyValue.Type() { - continue - } - - if dstValue.Field(i).CanSet() { - dstValue.Field(i).Set(propertyValue) - } + bytes, err := json.Marshal(src) + if err != nil { + return fmt.Errorf("CopyProperties: unable to marshal src: %s", err) + } + err = json.Unmarshal(bytes, dst) + if err != nil { + return fmt.Errorf("CopyProperties: unable to unmarshal into dst: %s", err) } return nil } + +// ToInterface converts reflect value to its interface type. +func ToInterface(v reflect.Value) (value interface{}, ok bool) { + if v.IsValid() && v.CanInterface() { + return v.Interface(), true + } + switch v.Kind() { + case reflect.Bool: + return v.Bool(), true + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int(), true + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint(), true + case reflect.Float32, reflect.Float64: + return v.Float(), true + case reflect.Complex64, reflect.Complex128: + return v.Complex(), true + case reflect.String: + return v.String(), true + case reflect.Ptr: + return ToInterface(v.Elem()) + case reflect.Interface: + return ToInterface(v.Elem()) + default: + return nil, false + } +} diff --git a/convertor/convertor_test.go b/convertor/convertor_test.go index 8d348eb..91b6760 100644 --- a/convertor/convertor_test.go +++ b/convertor/convertor_test.go @@ -262,49 +262,80 @@ func TestDeepClone(t *testing.T) { func TestCopyProperties(t *testing.T) { assert := internal.NewAssert(t, "TestCopyProperties") - type Address struct { - Country string - ZipCode string + type Disk struct { + Name string `json:"name"` + Total string `json:"total"` + Used string `json:"used"` + Percent float64 `json:"percent"` } - type User struct { - Name string - Age int - Role string - Addr Address - Hobbys []string - salary int + type DiskVO struct { + Name string `json:"name"` + Total string `json:"total"` + Used string `json:"used"` + Percent float64 `json:"percent"` } - type Employee struct { - Name string - Age int - Role string - Addr Address - Hobbys []string - salary int + type Indicator struct { + Id string `json:"id"` + Ip string `json:"ip"` + UpTime string `json:"upTime"` + LoadAvg string `json:"loadAvg"` + Cpu int `json:"cpu"` + Disk []Disk `json:"disk"` + Stop chan bool `json:"-"` } - user := User{Name: "user001", Age: 10, Role: "Admin", Addr: Address{Country: "CN", ZipCode: "001"}, Hobbys: []string{"a", "b"}, salary: 1000} + type IndicatorVO struct { + Id string `json:"id"` + Ip string `json:"ip"` + UpTime string `json:"upTime"` + LoadAvg string `json:"loadAvg"` + Cpu int64 `json:"cpu"` + Disk []DiskVO `json:"disk"` + } - employee1 := Employee{} + indicator := &Indicator{Id: "001", Ip: "127.0.0.1", Cpu: 1, Disk: []Disk{ + {Name: "disk-001", Total: "100", Used: "1", Percent: 10}, + {Name: "disk-002", Total: "200", Used: "1", Percent: 20}, + {Name: "disk-003", Total: "300", Used: "1", Percent: 30}, + }} - err := CopyProperties(&employee1, &user) + indicatorVO := IndicatorVO{} + + err := CopyProperties(&indicatorVO, indicator) assert.IsNil(err) - assert.Equal("user001", employee1.Name) - assert.Equal("Admin", employee1.Role) - assert.Equal("CN", employee1.Addr.Country) - assert.Equal(0, employee1.salary) - - employee2 := Employee{Name: "employee001", Age: 20, Role: "User", - Addr: Address{Country: "UK", ZipCode: "002"}, salary: 500} - - err = CopyProperties(&employee2, &user) - - assert.IsNil(err) - assert.Equal("user001", employee2.Name) - assert.Equal("Admin", employee2.Role) - assert.Equal("CN", employee2.Addr.Country) - assert.Equal(500, employee2.salary) + assert.Equal("001", indicatorVO.Id) + assert.Equal("127.0.0.1", indicatorVO.Ip) + assert.Equal(3, len(indicatorVO.Disk)) +} + +func TestToInterface(t *testing.T) { + assert := internal.NewAssert(t, "TestToInterface") + + cases := []reflect.Value{ + reflect.ValueOf("abc"), + reflect.ValueOf(int(0)), reflect.ValueOf(int8(1)), reflect.ValueOf(int16(-1)), reflect.ValueOf(int32(123)), reflect.ValueOf(int64(123)), + reflect.ValueOf(uint(123)), reflect.ValueOf(uint8(123)), reflect.ValueOf(uint16(123)), reflect.ValueOf(uint32(123)), reflect.ValueOf(uint64(123)), + reflect.ValueOf(float64(12.3)), reflect.ValueOf(float32(12.3)), + reflect.ValueOf(true), reflect.ValueOf(false), + } + + expected := []interface{}{ + "abc", + 0, int8(1), int16(-1), int32(123), int64(123), + uint(123), uint8(123), uint16(123), uint32(123), uint64(123), + float64(12.3), float32(12.3), + true, false, + } + + for i := 0; i < len(cases); i++ { + actual, _ := ToInterface(cases[i]) + assert.Equal(expected[i], actual) + } + + nilVal, ok := ToInterface(reflect.ValueOf(nil)) + assert.EqualValues(nil, nilVal) + assert.Equal(false, ok) } diff --git a/docs/convertor.md b/docs/convertor.md index 056c967..5e9be22 100644 --- a/docs/convertor.md +++ b/docs/convertor.md @@ -36,6 +36,9 @@ import ( - [EncodeByte](#EncodeByte) - [DecodeByte](#DecodeByte) - [DeepClone](#DeepClone) +- [CopyProperties](#CopyProperties) +- [ToInterface](#ToInterface) +
@@ -501,3 +504,117 @@ func main() { // &{test 1 0.1 trueCopies each field from the source struct into the destination struct. Use json.Marshal/Unmarshal, so json tag should be set for fields of dst and src struct.
+ +Signature: + +```go +func CopyProperties(dst, src interface{}) error +``` + +Example: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/convertor" +) + +func main() { + type Disk struct { + Name string `json:"name"` + Total string `json:"total"` + Used string `json:"used"` + Percent float64 `json:"percent"` + } + + type DiskVO struct { + Name string `json:"name"` + Total string `json:"total"` + Used string `json:"used"` + Percent float64 `json:"percent"` + } + + type Indicator struct { + Id string `json:"id"` + Ip string `json:"ip"` + UpTime string `json:"upTime"` + LoadAvg string `json:"loadAvg"` + Cpu int `json:"cpu"` + Disk []Disk `json:"disk"` + Stop chan bool `json:"-"` + } + + type IndicatorVO struct { + Id string `json:"id"` + Ip string `json:"ip"` + UpTime string `json:"upTime"` + LoadAvg string `json:"loadAvg"` + Cpu int64 `json:"cpu"` + Disk []DiskVO `json:"disk"` + } + + indicator := &Indicator{Id: "001", Ip: "127.0.0.1", Cpu: 1, Disk: []Disk{ + {Name: "disk-001", Total: "100", Used: "1", Percent: 10}, + {Name: "disk-002", Total: "200", Used: "1", Percent: 20}, + {Name: "disk-003", Total: "300", Used: "1", Percent: 30}, + }} + + indicatorVO := IndicatorVO{} + + err := convertor.CopyProperties(&indicatorVO, indicator) + + if err != nil { + return + } + + fmt.Println(indicatorVO.Id) + fmt.Println(indicatorVO.Ip) + fmt.Println(len(indicatorVO.Disk)) + + // Output: + // 001 + // 127.0.0.1 + // 3 +} +``` + +### ToInterface + +Converts reflect value to its interface type.
+ +Signature: + +```go +func ToInterface(v reflect.Value) (value interface{}, ok bool) +``` + +Example: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/convertor" +) + +func main() { + val := reflect.ValueOf("abc") + iVal, ok := convertor.ToInterface(val) + + fmt.Printf("%T\n", iVal) + fmt.Printf("%v\n", iVal) + fmt.Println(ok) + + // Output: + // string + // abc + // true +} +``` \ No newline at end of file diff --git a/docs/convertor_zh-CN.md b/docs/convertor_zh-CN.md index 14b5520..afe15cf 100644 --- a/docs/convertor_zh-CN.md +++ b/docs/convertor_zh-CN.md @@ -36,6 +36,8 @@ import ( - [EncodeByte](#EncodeByte) - [DecodeByte](#DecodeByte) - [DeepClone](#DeepClone) +- [CopyProperties](#CopyProperties) +- [ToInterface](#ToInterface) @@ -501,3 +503,118 @@ func main() { // &{test 1 0.1 true拷贝不同结构体之间的同名字段。使用json.Marshal序列化,需要设置dst和src struct字段的json tag。
+ +函数签名: + +```go +func CopyProperties(dst, src interface{}) error +``` + +示例: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/convertor" +) + +func main() { + type Disk struct { + Name string `json:"name"` + Total string `json:"total"` + Used string `json:"used"` + Percent float64 `json:"percent"` + } + + type DiskVO struct { + Name string `json:"name"` + Total string `json:"total"` + Used string `json:"used"` + Percent float64 `json:"percent"` + } + + type Indicator struct { + Id string `json:"id"` + Ip string `json:"ip"` + UpTime string `json:"upTime"` + LoadAvg string `json:"loadAvg"` + Cpu int `json:"cpu"` + Disk []Disk `json:"disk"` + Stop chan bool `json:"-"` + } + + type IndicatorVO struct { + Id string `json:"id"` + Ip string `json:"ip"` + UpTime string `json:"upTime"` + LoadAvg string `json:"loadAvg"` + Cpu int64 `json:"cpu"` + Disk []DiskVO `json:"disk"` + } + + indicator := &Indicator{Id: "001", Ip: "127.0.0.1", Cpu: 1, Disk: []Disk{ + {Name: "disk-001", Total: "100", Used: "1", Percent: 10}, + {Name: "disk-002", Total: "200", Used: "1", Percent: 20}, + {Name: "disk-003", Total: "300", Used: "1", Percent: 30}, + }} + + indicatorVO := IndicatorVO{} + + err := convertor.CopyProperties(&indicatorVO, indicator) + + if err != nil { + return + } + + fmt.Println(indicatorVO.Id) + fmt.Println(indicatorVO.Ip) + fmt.Println(len(indicatorVO.Disk)) + + // Output: + // 001 + // 127.0.0.1 + // 3 +} +``` + +### ToInterface + +将反射值转换成对应的interface类型。
+ +函数签名: + +```go +func ToInterface(v reflect.Value) (value interface{}, ok bool) +``` + +示例: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/convertor" +) + +func main() { + val := reflect.ValueOf("abc") + iVal, ok := convertor.ToInterface(val) + + fmt.Printf("%T\n", iVal) + fmt.Printf("%v\n", iVal) + fmt.Println(ok) + + // Output: + // string + // abc + // true +} +``` \ No newline at end of file