Skip to content

Commit

Permalink
copier DeepCopy
Browse files Browse the repository at this point in the history
  • Loading branch information
wubin48435 committed Aug 22, 2024
1 parent 15371f7 commit 7524ff8
Show file tree
Hide file tree
Showing 3 changed files with 4 additions and 317 deletions.
2 changes: 1 addition & 1 deletion toolkit/caches/reflection.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
)

func SetPointedValue(dest interface{}, src interface{}) {
copier.DeepCopyAsJson(src, dest)
copier.DeepCopy(src, dest)
}

func deepCopy(src, dst interface{}) error {
Expand Down
217 changes: 3 additions & 214 deletions toolkit/copier/copier.go
Original file line number Diff line number Diff line change
@@ -1,22 +1,15 @@
package copier

import (
"bytes"
"fmt"
"github.com/spf13/cast"
"github.com/unionj-cloud/go-doudou/v2/toolkit/zlogger"
"reflect"
"strings"

"github.com/bytedance/sonic"
"github.com/bytedance/sonic/decoder"
"github.com/pkg/errors"
"reflect"
)

var json = sonic.ConfigDefault

// DeepCopyAsJson src to target with json marshal and unmarshal
func DeepCopyAsJson(src, target interface{}) error {
// DeepCopy src to target with json marshal and unmarshal
func DeepCopy(src, target interface{}) error {
if src == nil || target == nil {
return nil
}
Expand All @@ -29,207 +22,3 @@ func DeepCopyAsJson(src, target interface{}) error {
}
return json.Unmarshal(b, target)
}

// DeepCopy src to target with json marshal and unmarshal
func DeepCopy(src, target interface{}) error {
if src == nil || target == nil {
return nil
}
if reflect.ValueOf(target).Kind() != reflect.Ptr {
return errors.New("Target should be a pointer")
}
switch value := src.(type) {
case *map[string]interface{}:
if value == nil {
return nil
}
return MapToStruct(*value, target)
case map[string]interface{}:
return MapToStruct(value, target)
default:
b, err := json.Marshal(src)
if err != nil {
return errors.WithStack(err)
}
dec := decoder.NewStreamDecoder(bytes.NewReader(b))
dec.UseInt64()
return dec.Decode(target)
}
}

func MapToStruct(m map[string]any, structObj any) error {
for k, v := range m {
err := setStructField(structObj, k, v)
if err != nil {
return err
}
}

return nil
}

func setStructField(structObj any, fieldName string, fieldValue any) error {
structVal := reflect.ValueOf(structObj).Elem()

fName := getFieldNameByJsonTag(structObj, fieldName)
if fName == "" {
return nil
}

fieldVal := structVal.FieldByName(fName)

if !fieldVal.IsValid() {
return fmt.Errorf("No such field: %s in obj", fieldName)
}

if !fieldVal.CanSet() {
return fmt.Errorf("Cannot set %s field value", fieldName)
}

val := reflect.ValueOf(fieldValue)

if !val.IsValid() {
return nil
}

if fieldVal.Type() != val.Type() {

if val.CanConvert(fieldVal.Type()) {
fieldVal.Set(val.Convert(fieldVal.Type()))
return nil
}

if fieldVal.Kind() == reflect.Ptr {
v := reflect.New(fieldVal.Type().Elem())
if fieldVal.Type().Elem() != val.Type() {
if val.CanConvert(fieldVal.Type().Elem()) {
v.Elem().Set(val.Convert(fieldVal.Type().Elem()))
fieldVal.Set(v)
return nil
}
} else {
v.Elem().Set(val)
fieldVal.Set(v)
return nil
}
}

if val.Kind() == reflect.Ptr {
v := val.Elem()
if fieldVal.Type() != v.Type() {
if v.CanConvert(fieldVal.Type()) {
fieldVal.Set(v.Convert(fieldVal.Type()))
return nil
}
} else {
fieldVal.Set(v)
return nil
}
}

if val.Kind() == reflect.String {
switch fieldVal.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
fieldVal.SetInt(cast.ToInt64(val.String()))
return nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
fieldVal.SetUint(cast.ToUint64(val.String()))
return nil
case reflect.Float32, reflect.Float64:
fieldVal.SetFloat(cast.ToFloat64(val.String()))
return nil
case reflect.Ptr:
v := reflect.New(fieldVal.Type().Elem())
switch fieldVal.Type().Elem().Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
v.Elem().SetInt(cast.ToInt64(val.String()))
fieldVal.Set(v)
return nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
v.Elem().SetUint(cast.ToUint64(val.String()))
fieldVal.Set(v)
return nil
case reflect.Float32, reflect.Float64:
v.Elem().SetFloat(cast.ToFloat64(val.String()))
fieldVal.Set(v)
return nil
}
}
} else if val.Kind() == reflect.Ptr && val.Type().Elem().Kind() == reflect.String {
underlyingV := val.Elem().String()
switch fieldVal.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
fieldVal.SetInt(cast.ToInt64(underlyingV))
return nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
fieldVal.SetUint(cast.ToUint64(underlyingV))
return nil
case reflect.Float32, reflect.Float64:
fieldVal.SetFloat(cast.ToFloat64(underlyingV))
return nil
case reflect.Ptr:
v := reflect.New(fieldVal.Type().Elem())
switch fieldVal.Type().Elem().Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
v.Elem().SetInt(cast.ToInt64(underlyingV))
fieldVal.Set(v)
return nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
v.Elem().SetUint(cast.ToUint64(underlyingV))
fieldVal.Set(v)
return nil
case reflect.Float32, reflect.Float64:
v.Elem().SetFloat(cast.ToFloat64(underlyingV))
fieldVal.Set(v)
return nil
}
}
}

if m, ok := fieldValue.(map[string]any); ok {

if fieldVal.Kind() == reflect.Struct {
return MapToStruct(m, fieldVal.Addr().Interface())
}

if fieldVal.Kind() == reflect.Ptr && fieldVal.Type().Elem().Kind() == reflect.Struct {
if fieldVal.IsNil() {
fieldVal.Set(reflect.New(fieldVal.Type().Elem()))
}

return MapToStruct(m, fieldVal.Interface())
}
}

if j, err := json.MarshalToString(fieldValue); err == nil {
v := reflect.New(fieldVal.Type())
if err := json.UnmarshalFromString(j, v.Interface()); err == nil {
fieldVal.Set(v.Elem())
return nil
} else {
zlogger.Error().Err(err).Msg(err.Error())
}
}

return fmt.Errorf("map attribute [%s] value type don't match struct field [%s] type", fieldName, fName)
}

fieldVal.Set(val)

return nil
}

func getFieldNameByJsonTag(structObj any, jsonTag string) string {
s := reflect.TypeOf(structObj).Elem()

for i := 0; i < s.NumField(); i++ {
field := s.Field(i)
tag := field.Tag
name, _, _ := strings.Cut(tag.Get("json"), ",")
if name == jsonTag {
return field.Name
}
}

return ""
}
102 changes: 0 additions & 102 deletions toolkit/copier/copier_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@ package copier

import (
"fmt"
"github.com/samber/lo"
"github.com/unionj-cloud/go-doudou/v2/toolkit/customtypes"
"github.com/wubin1989/gorm"
"testing"
)

Expand Down Expand Up @@ -144,102 +141,3 @@ func TestDeepCopy_ShouldHasError(t *testing.T) {
})
}
}

func TestDeepCopy2(t *testing.T) {
t1 := `{"name":"jack", "age": "18.0"}`
type Person struct {
Name string
Age float64 `json:"age,string"`
}
var p Person
json.Unmarshal([]byte(t1), &p)

type Student struct {
Name string
Age int `json:"age,string"`
}
var s Student
DeepCopy(p, &s)

fmt.Println(p)
fmt.Println(s)

// Output:
// {jack 18}
//{jack 18}

}

func TestDeepCopy3(t *testing.T) {
//t1 := `{"name":"jack", "age": 18.0, "school": "beijing"}`
p := make(map[string]interface{})
p["name"] = nil
p["age"] = lo.ToPtr(18)
//dec := decoder.NewDecoder(t1)
//dec.UseInt64()
//dec.Decode(&p)
//ddd, _ := json.Marshal(p)
//fmt.Println(string(ddd))
type Student struct {
Name *string `json:"name"`
Age *int `json:"age,string"`
}
var s Student
if err := DeepCopy(p, &s); err != nil {
panic(err)
}

fmt.Println(p)
fmt.Println(s)

// Output:
// {jack 18}
//{jack 18}

}

func TestDeepCopy4(t *testing.T) {
//t1 := `{"name":"jack", "age": 18.0, "school": "beijing"}`
p := make(map[string]interface{})
p["name"] = nil
p["age"] = "18"
type Student struct {
Name *string `json:"name"`
Age *int64 `json:"age,string"`
}
var s Student
if err := DeepCopy(p, &s); err != nil {
panic(err)
}

fmt.Println(p)
fmt.Println(s)

// Output:
// {jack 18}
//{jack 18}

}

func TestDeepCopy5(t *testing.T) {
t1 := `{"updated_at":"2024-07-27 13:34:27","deleted_at":"2006-01-02T15:04:05Z"}`
p := make(map[string]interface{})
json.Unmarshal([]byte(t1), &p)

type Student struct {
UpdatedAt *customtypes.Time `json:"updated_at"`
DeletedAt gorm.DeletedAt `json:"deleted_at"`
}
var s Student
if err := DeepCopy(p, &s); err != nil {
panic(err)
}

fmt.Println(p)
fmt.Println(s)

// Output:
// {jack 18}
//{jack 18}

}

0 comments on commit 7524ff8

Please sign in to comment.