// Copyright 2019 github.com. All rights reserved. // Use of this source code is governed by github.com. package utils import ( "errors" "reflect" ) // check 检查参数 func check(data interface{}) error { if reflect.TypeOf(data).Kind() != reflect.Ptr { return errors.New("param must be ptr") } if reflect.ValueOf(data).IsNil() { return errors.New("param can not be nil") } if reflect.TypeOf(data).Elem().Kind() == reflect.Ptr { return errors.New("param must be ptr") } return nil } // getFieldArray 获取结构体元素数组 func getFieldArray(t reflect.Type) (ret []reflect.StructField) { for i := 0; i < t.NumField(); i++ { field := t.Field(i) ret = append(ret, field) } return ret } // FieldByTag 根据tag名(tagName)和tag值(tag)从结构体重获取元素 // 如果tag或tagName为空,则根据元素名称获取元素 func FieldByTag(value reflect.Value, name string, tag string, tagName string) reflect.Value { if tag == "" || tagName == "" { return value.FieldByName(name) } itype := value.Type() for i := 0; i < itype.NumField(); i++ { v := itype.Field(i) if v.Tag.Get(tagName) == tag { return value.FieldByIndex(v.Index) } } return reflect.Value{} } // subStructCopy copy结构体中的子结构体 func subStructCopy(dst reflect.Value, src reflect.Value, tagName string) { fields := getFieldArray(src.Type()) for _, v := range fields { // 获取元素 srcf := FieldByTag(src, v.Name, v.Tag.Get(tagName), tagName) dstf := FieldByTag(dst, v.Name, v.Tag.Get(tagName), tagName) // 是否能赋值 if dstf.CanSet() == false { continue } // 如果元素是结构体,递归 if v.Type.Kind() == reflect.Struct { subStructCopy(dstf, srcf, tagName) continue } if dstf.Type() != srcf.Type() { // 源和目标类型不一致,基础类型为切片且元素都为结构体,进行切片拷贝 if v.Type.Kind() == reflect.Slice && dstf.Type().Kind() == reflect.Slice && srcf.Type().Kind() == reflect.Slice && dstf.Type().Elem().Kind() == reflect.Struct && srcf.Type().Elem().Kind() == reflect.Struct { array := reflect.MakeSlice(dstf.Type(), 0, 0) for i := 0; i < srcf.Len(); i++ { item := reflect.New(dstf.Type().Elem()) subStructCopy(item.Elem(), srcf.Index(i), tagName) array = reflect.Append(array, item.Elem()) } dstf.Set(array) } continue } // 拷贝指针指向的数据 if dstf.Kind() == reflect.Ptr && srcf.IsNil() == false { dstf.Set(reflect.New(dstf.Type().Elem())) dstf.Elem().Set(srcf.Elem()) continue } // 普通数据拷贝 dstf.Set(srcf) } } // StructCopy 根据tagName(如json)进行结构体拷贝 // tagName为空,默认按结构体元素名进行拷贝 func StructCopy(dst interface{}, src interface{}, tagName string) error { if err := check(dst); err != nil { return err } if err := check(src); err != nil { return err } srcv := reflect.ValueOf(src).Elem() dstv := reflect.ValueOf(dst).Elem() subStructCopy(dstv, srcv, tagName) return nil }