1
0
mirror of https://github.com/duke-git/lancet.git synced 2026-03-01 00:35:28 +08:00

fix: refact CopyProperties

This commit is contained in:
dudaodong
2023-04-22 14:14:13 +08:00
parent 48c17ea1ce
commit aaf45012e4
5 changed files with 172 additions and 153 deletions

View File

@@ -11,11 +11,12 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"github.com/duke-git/lancet/v2/structs"
"math" "math"
"reflect" "reflect"
"strconv" "strconv"
"strings" "strings"
"github.com/duke-git/lancet/v2/structs"
) )
// ToBool convert string to boolean. // 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. // 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 // use json.Marshal/Unmarshal, so json tag should be set for fields of dst and src struct.
func CopyProperties[T, U any](dst T, src U) (err error) { // Play: todo
defer func() { func CopyProperties[T, U any](dst T, src U) error {
if e := recover(); e != nil { dstType, srcType := reflect.TypeOf(dst), reflect.TypeOf(src)
err = fmt.Errorf("%v", e)
}
}()
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 { 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 { if srcType.Kind() == reflect.Ptr {
srcType, srcValue = srcType.Elem(), srcValue.Elem() srcType = srcType.Elem()
} }
if srcType.Kind() != reflect.Struct { 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() bytes, err := json.Marshal(src)
if err != nil {
propertyNums := dstType.NumField() return fmt.Errorf("CopyProperties: unable to marshal src: %s", err)
}
for i := 0; i < propertyNums; i++ { err = json.Unmarshal(bytes, dst)
property := dstType.Field(i) if err != nil {
propertyValue := srcValue.FieldByName(property.Name) return fmt.Errorf("CopyProperties: unable to unmarshal into dst: %s", err)
if !propertyValue.IsValid() || property.Type != propertyValue.Type() {
continue
}
if dstValue.Field(i).CanSet() {
dstValue.Field(i).Set(propertyValue)
}
} }
return nil return nil

View File

@@ -297,49 +297,55 @@ func ExampleDeepClone() {
} }
func ExampleCopyProperties() { func ExampleCopyProperties() {
type Address struct { type Disk struct {
Country string Name string `json:"name"`
ZipCode string Total string `json:"total"`
Used string `json:"used"`
Percent float64 `json:"percent"`
} }
type User struct { type DiskVO struct {
Name string Name string `json:"name"`
Age int Total string `json:"total"`
Role string Used string `json:"used"`
Addr Address Percent float64 `json:"percent"`
Hobbys []string
salary int
} }
type Employee struct { type Indicator struct {
Name string Id string `json:"id"`
Age int Ip string `json:"ip"`
Role string UpTime string `json:"upTime"`
Addr Address LoadAvg string `json:"loadAvg"`
Hobbys []string Cpu int `json:"cpu"`
salary int 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"`
employee1 := Employee{} Ip string `json:"ip"`
err := CopyProperties(&employee1, &user) UpTime string `json:"upTime"`
if err != nil { LoadAvg string `json:"loadAvg"`
return Cpu int64 `json:"cpu"`
Disk []DiskVO `json:"disk"`
} }
employee2 := Employee{Name: "employee001", Age: 20, Role: "User", indicator := &Indicator{Id: "001", Ip: "127.0.0.1", Cpu: 1, Disk: []Disk{
Addr: Address{Country: "UK", ZipCode: "002"}, salary: 500} {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) indicatorVO := IndicatorVO{}
if err != nil {
return
}
fmt.Println(employee1) CopyProperties(&indicatorVO, indicator)
fmt.Println(employee2)
fmt.Println(indicatorVO.Id)
fmt.Println(indicatorVO.Ip)
fmt.Println(len(indicatorVO.Disk))
// Output: // Output:
// {user001 10 Admin {CN 001} [a b] 0} // 001
// {user001 10 Admin {CN 001} [a b] 500} // 127.0.0.1
// 3
} }

View File

@@ -320,49 +320,51 @@ func TestDeepClone(t *testing.T) {
func TestCopyProperties(t *testing.T) { func TestCopyProperties(t *testing.T) {
assert := internal.NewAssert(t, "TestCopyProperties") assert := internal.NewAssert(t, "TestCopyProperties")
type Address struct { type Disk struct {
Country string Name string `json:"name"`
ZipCode string Total string `json:"total"`
Used string `json:"used"`
Percent float64 `json:"percent"`
} }
type User struct { type DiskVO struct {
Name string Name string `json:"name"`
Age int Total string `json:"total"`
Role string Used string `json:"used"`
Addr Address Percent float64 `json:"percent"`
Hobbys []string
salary int
} }
type Employee struct { type Indicator struct {
Name string Id string `json:"id"`
Age int Ip string `json:"ip"`
Role string UpTime string `json:"upTime"`
Addr Address LoadAvg string `json:"loadAvg"`
Hobbys []string Cpu int `json:"cpu"`
salary int 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.IsNil(err)
assert.Equal("user001", employee1.Name) assert.Equal("001", indicatorVO.Id)
assert.Equal("Admin", employee1.Role) assert.Equal("127.0.0.1", indicatorVO.Ip)
assert.Equal("CN", employee1.Addr.Country) assert.Equal(3, len(indicatorVO.Disk))
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)
} }

View File

@@ -697,7 +697,7 @@ func main() {
### <span id="CopyProperties">CopyProperties</span> ### <span id="CopyProperties">CopyProperties</span>
<p>Copies each field from the source struct into the destination struct.</p> <p>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.</p>
<b>Signature:</b> <b>Signature:</b>
@@ -716,44 +716,56 @@ import (
) )
func main() { func main() {
type Address struct { type Disk struct {
Country string Name string `json:"name"`
ZipCode string Total string `json:"total"`
Used string `json:"used"`
Percent float64 `json:"percent"`
} }
type User struct { type DiskVO struct {
Name string Name string `json:"name"`
Age int Total string `json:"total"`
Role string Used string `json:"used"`
Addr Address Percent float64 `json:"percent"`
Hobbys []string
salary int
} }
type Employee struct { type Indicator struct {
Name string Id string `json:"id"`
Age int Ip string `json:"ip"`
Role string UpTime string `json:"upTime"`
Addr Address LoadAvg string `json:"loadAvg"`
Hobbys []string Cpu int `json:"cpu"`
salary int 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{
CopyProperties(&employee1, &user) {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", indicatorVO := IndicatorVO{}
Addr: Address{Country: "UK", ZipCode: "002"}, salary: 500}
CopyProperties(&employee2, &user) CopyProperties(&indicatorVO, indicator)
fmt.Println(employee1) fmt.Println(indicatorVO.Id)
fmt.Println(employee2) fmt.Println(indicatorVO.Ip)
fmt.Println(len(indicatorVO.Disk))
// Output: // Output:
// {user001 10 Admin {CN 001} [a b] 0} // 001
// {user001 10 Admin {CN 001} [a b] 500} // 127.0.0.1
// 3
} }
``` ```

View File

@@ -696,7 +696,7 @@ func main() {
### <span id="CopyProperties">CopyProperties</span> ### <span id="CopyProperties">CopyProperties</span>
<p>拷贝不同结构体之间的同名字段。</p> <p>拷贝不同结构体之间的同名字段。使用json.Marshal序列化需要设置dst和src struct字段的json tag。</p>
<b>函数签名:</b> <b>函数签名:</b>
@@ -715,44 +715,56 @@ import (
) )
func main() { func main() {
type Address struct { type Disk struct {
Country string Name string `json:"name"`
ZipCode string Total string `json:"total"`
Used string `json:"used"`
Percent float64 `json:"percent"`
} }
type User struct { type DiskVO struct {
Name string Name string `json:"name"`
Age int Total string `json:"total"`
Role string Used string `json:"used"`
Addr Address Percent float64 `json:"percent"`
Hobbys []string
salary int
} }
type Employee struct { type Indicator struct {
Name string Id string `json:"id"`
Age int Ip string `json:"ip"`
Role string UpTime string `json:"upTime"`
Addr Address LoadAvg string `json:"loadAvg"`
Hobbys []string Cpu int `json:"cpu"`
salary int 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{
CopyProperties(&employee1, &user) {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", indicatorVO := IndicatorVO{}
Addr: Address{Country: "UK", ZipCode: "002"}, salary: 500}
CopyProperties(&employee2, &user) CopyProperties(&indicatorVO, indicator)
fmt.Println(employee1) fmt.Println(indicatorVO.Id)
fmt.Println(employee2) fmt.Println(indicatorVO.Ip)
fmt.Println(len(indicatorVO.Disk))
// Output: // Output:
// {user001 10 Admin {CN 001} [a b] 0} // 001
// {user001 10 Admin {CN 001} [a b] 500} // 127.0.0.1
// 3
} }
``` ```