123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189 |
- package utilx
- import (
- "database/sql"
- "errors"
- "reflect"
- )
- // Copy 复制结构体内容
- func Copy(toValue interface{}, fromValue interface{}) (err error) {
- var (
- isSlice bool
- amount = 1
- from = indirect(reflect.ValueOf(fromValue))
- to = indirect(reflect.ValueOf(toValue))
- )
- if !to.CanAddr() {
- return errors.New("copy to value is unaddressable")
- }
- // Return is from value is invalid
- if !from.IsValid() {
- return
- }
- fromType := indirectType(from.Type())
- toType := indirectType(to.Type())
- // Just set it if possible to assign
- // And need to do copy anyway if the type is struct
- if fromType.Kind() != reflect.Struct && from.Type().AssignableTo(to.Type()) {
- to.Set(from)
- return
- }
- if fromType.Kind() != reflect.Struct || toType.Kind() != reflect.Struct {
- return
- }
- if to.Kind() == reflect.Slice {
- isSlice = true
- if from.Kind() == reflect.Slice {
- amount = from.Len()
- }
- }
- for i := 0; i < amount; i++ {
- var dest, source reflect.Value
- if isSlice {
- // source
- if from.Kind() == reflect.Slice {
- source = indirect(from.Index(i))
- } else {
- source = indirect(from)
- }
- // dest
- dest = indirect(reflect.New(toType).Elem())
- } else {
- source = indirect(from)
- dest = indirect(to)
- }
- // check source
- if source.IsValid() {
- fromTypeFields := deepFields(fromType)
- //fmt.Printf("%#v", fromTypeFields)
- // Copy from field to field or method
- for _, field := range fromTypeFields {
- name := field.Name
- if fromField := source.FieldByName(name); fromField.IsValid() {
- // has field
- if toField := dest.FieldByName(name); toField.IsValid() {
- if toField.CanSet() {
- if !set(toField, fromField) {
- if err := Copy(toField.Addr().Interface(), fromField.Interface()); err != nil {
- return err
- }
- }
- }
- } else {
- // try to set to method
- var toMethod reflect.Value
- if dest.CanAddr() {
- toMethod = dest.Addr().MethodByName(name)
- } else {
- toMethod = dest.MethodByName(name)
- }
- if toMethod.IsValid() && toMethod.Type().NumIn() == 1 && fromField.Type().AssignableTo(toMethod.Type().In(0)) {
- toMethod.Call([]reflect.Value{fromField})
- }
- }
- }
- }
- // Copy from method to field
- for _, field := range deepFields(toType) {
- name := field.Name
- var fromMethod reflect.Value
- if source.CanAddr() {
- fromMethod = source.Addr().MethodByName(name)
- } else {
- fromMethod = source.MethodByName(name)
- }
- if fromMethod.IsValid() && fromMethod.Type().NumIn() == 0 && fromMethod.Type().NumOut() == 1 {
- if toField := dest.FieldByName(name); toField.IsValid() && toField.CanSet() {
- values := fromMethod.Call([]reflect.Value{})
- if len(values) >= 1 {
- set(toField, values[0])
- }
- }
- }
- }
- }
- if isSlice {
- if dest.Addr().Type().AssignableTo(to.Type().Elem()) {
- to.Set(reflect.Append(to, dest.Addr()))
- } else if dest.Type().AssignableTo(to.Type().Elem()) {
- to.Set(reflect.Append(to, dest))
- }
- }
- }
- return
- }
- func deepFields(reflectType reflect.Type) []reflect.StructField {
- var fields []reflect.StructField
- if reflectType = indirectType(reflectType); reflectType.Kind() == reflect.Struct {
- for i := 0; i < reflectType.NumField(); i++ {
- v := reflectType.Field(i)
- if v.Anonymous {
- fields = append(fields, deepFields(v.Type)...)
- } else {
- fields = append(fields, v)
- }
- }
- }
- return fields
- }
- func indirect(reflectValue reflect.Value) reflect.Value {
- for reflectValue.Kind() == reflect.Ptr {
- reflectValue = reflectValue.Elem()
- }
- return reflectValue
- }
- func indirectType(reflectType reflect.Type) reflect.Type {
- for reflectType.Kind() == reflect.Ptr || reflectType.Kind() == reflect.Slice {
- reflectType = reflectType.Elem()
- }
- return reflectType
- }
- func set(to, from reflect.Value) bool {
- if from.IsValid() {
- if to.Kind() == reflect.Ptr {
- //set `to` to nil if from is nil
- if from.Kind() == reflect.Ptr && from.IsNil() {
- to.Set(reflect.Zero(to.Type()))
- return true
- } else if to.IsNil() {
- to.Set(reflect.New(to.Type().Elem()))
- }
- to = to.Elem()
- }
- if from.Type().ConvertibleTo(to.Type()) {
- to.Set(from.Convert(to.Type()))
- } else if scanner, ok := to.Addr().Interface().(sql.Scanner); ok {
- err := scanner.Scan(from.Interface())
- if err != nil {
- return false
- }
- } else if from.Kind() == reflect.Ptr {
- return set(to, from.Elem())
- } else {
- return false
- }
- }
- return true
- }
|