From 7524ff83f511ff933c46597c1d89892610485668 Mon Sep 17 00:00:00 2001 From: wubin48435 Date: Thu, 22 Aug 2024 16:04:54 +0800 Subject: [PATCH] copier DeepCopy --- toolkit/caches/reflection.go | 2 +- toolkit/copier/copier.go | 217 +--------------------------------- toolkit/copier/copier_test.go | 102 ---------------- 3 files changed, 4 insertions(+), 317 deletions(-) diff --git a/toolkit/caches/reflection.go b/toolkit/caches/reflection.go index cda21929..cececda3 100644 --- a/toolkit/caches/reflection.go +++ b/toolkit/caches/reflection.go @@ -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 { diff --git a/toolkit/copier/copier.go b/toolkit/copier/copier.go index ec67557d..c469f3be 100644 --- a/toolkit/copier/copier.go +++ b/toolkit/copier/copier.go @@ -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 } @@ -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 "" -} diff --git a/toolkit/copier/copier_test.go b/toolkit/copier/copier_test.go index 13fe9185..d1029504 100644 --- a/toolkit/copier/copier_test.go +++ b/toolkit/copier/copier_test.go @@ -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" ) @@ -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} - -}