diff --git a/convertor/convertor.go b/convertor/convertor.go index c5373be..2035a49 100644 --- a/convertor/convertor.go +++ b/convertor/convertor.go @@ -11,11 +11,12 @@ import ( "encoding/json" "errors" "fmt" - "github.com/duke-git/lancet/v2/structs" "math" "reflect" "strconv" "strings" + + "github.com/duke-git/lancet/v2/structs" ) // ToBool convert string to boolean. @@ -319,43 +320,29 @@ func DeepClone[T any](src T) T { } // CopyProperties copies each field from the source into the destination. It recursively copies struct pointers and interfaces that contain struct pointers. -// Play: https://go.dev/play/p/FOVY3XJL-6B -func CopyProperties[T, U any](dst T, src U) (err error) { - defer func() { - if e := recover(); e != nil { - err = fmt.Errorf("%v", e) - } - }() - - dstType, dstValue := reflect.TypeOf(dst), reflect.ValueOf(dst) - srcType, srcValue := reflect.TypeOf(src), reflect.ValueOf(src) +// use json.Marshal/Unmarshal, so json tag should be set for fields of dst and src struct. +// Play: todo +func CopyProperties[T, U any](dst T, src U) 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 diff --git a/convertor/convertor_example_test.go b/convertor/convertor_example_test.go index a1db8e5..0caa26a 100644 --- a/convertor/convertor_example_test.go +++ b/convertor/convertor_example_test.go @@ -297,49 +297,55 @@ func ExampleDeepClone() { } func ExampleCopyProperties() { - 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} - - employee1 := Employee{} - err := CopyProperties(&employee1, &user) - if err != nil { - return + 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"` } - employee2 := Employee{Name: "employee001", Age: 20, Role: "User", - Addr: Address{Country: "UK", ZipCode: "002"}, salary: 500} + 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(&employee2, &user) - if err != nil { - return - } + indicatorVO := IndicatorVO{} - fmt.Println(employee1) - fmt.Println(employee2) + CopyProperties(&indicatorVO, indicator) + + fmt.Println(indicatorVO.Id) + fmt.Println(indicatorVO.Ip) + fmt.Println(len(indicatorVO.Disk)) // Output: - // {user001 10 Admin {CN 001} [a b] 0} - // {user001 10 Admin {CN 001} [a b] 500} + // 001 + // 127.0.0.1 + // 3 } diff --git a/convertor/convertor_test.go b/convertor/convertor_test.go index 76ef550..08fd5f4 100644 --- a/convertor/convertor_test.go +++ b/convertor/convertor_test.go @@ -320,49 +320,51 @@ 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)) } diff --git a/docs/convertor.md b/docs/convertor.md index b49fc56..527ae78 100644 --- a/docs/convertor.md +++ b/docs/convertor.md @@ -697,7 +697,7 @@ func main() { ### CopyProperties -
Copies each field from the source struct into the destination struct.
+Copies 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: @@ -716,44 +716,56 @@ import ( ) func main() { - 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{} - CopyProperties(&employee1, &user) + 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}, + }} - employee2 := Employee{Name: "employee001", Age: 20, Role: "User", - Addr: Address{Country: "UK", ZipCode: "002"}, salary: 500} + indicatorVO := IndicatorVO{} - CopyProperties(&employee2, &user) + CopyProperties(&indicatorVO, indicator) - fmt.Println(employee1) - fmt.Println(employee2) + fmt.Println(indicatorVO.Id) + fmt.Println(indicatorVO.Ip) + fmt.Println(len(indicatorVO.Disk)) // Output: - // {user001 10 Admin {CN 001} [a b] 0} - // {user001 10 Admin {CN 001} [a b] 500} + // 001 + // 127.0.0.1 + // 3 } ``` \ No newline at end of file diff --git a/docs/convertor_zh-CN.md b/docs/convertor_zh-CN.md index 9257092..8d38744 100644 --- a/docs/convertor_zh-CN.md +++ b/docs/convertor_zh-CN.md @@ -696,7 +696,7 @@ func main() { ### CopyProperties -拷贝不同结构体之间的同名字段。
+拷贝不同结构体之间的同名字段。使用json.Marshal序列化,需要设置dst和src struct字段的json tag。
函数签名: @@ -715,44 +715,56 @@ import ( ) func main() { - 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{} - CopyProperties(&employee1, &user) + 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}, + }} - employee2 := Employee{Name: "employee001", Age: 20, Role: "User", - Addr: Address{Country: "UK", ZipCode: "002"}, salary: 500} + indicatorVO := IndicatorVO{} - CopyProperties(&employee2, &user) + CopyProperties(&indicatorVO, indicator) - fmt.Println(employee1) - fmt.Println(employee2) + fmt.Println(indicatorVO.Id) + fmt.Println(indicatorVO.Ip) + fmt.Println(len(indicatorVO.Disk)) // Output: - // {user001 10 Admin {CN 001} [a b] 0} - // {user001 10 Admin {CN 001} [a b] 500} + // 001 + // 127.0.0.1 + // 3 } ``` \ No newline at end of file