Go语言不同结构体相同字段名,进行值转换
问题:下面定义Student和Teacher两个结构体,如何让他们的数据值转换呢?
type Student struct { Id int `json:"id"` Name string `json:"name"` Slic []int `json:"slic"` S struct { Id int `json:"id"` Name string `json:"name"` } `json:"s"` } type Teacher struct { Id int `json:"id"` Name string `json:"name"` Slic []int `json:"slic"` S struct { Id int `json:"id"` Name string `json:"name"` } `json:"s"` }
小问题,还可以很优雅:
// StructToStruct 结构体转结构体
func StructToStruct(sourceStruct, targetStruct interface{}, cover bool) (err error) {
sourceType := reflect.TypeOf(sourceStruct)
targetType := reflect.TypeOf(targetStruct)
if targetType.Kind() != reflect.Ptr {
err = errors.New("转换失败,目标结构体不是指针")
return
}
for sourceType.Kind() == reflect.Ptr {
sourceType = sourceType.Elem()
}
for targetType.Kind() == reflect.Ptr {
targetType = targetType.Elem()
}
if sourceType.Kind() != reflect.Struct || targetType.Kind() != reflect.Struct {
err = errors.New("转换失败,只支持转结构体")
return
}
sourceValue := reflect.ValueOf(sourceStruct)
targetValue := reflect.ValueOf(targetStruct)
for sourceValue.Kind() == reflect.Ptr {
sourceValue = sourceValue.Elem()
}
for targetValue.Kind() == reflect.Ptr {
targetValue = targetValue.Elem()
}
ok := transformation(sourceType, targetType, sourceValue, targetValue, cover)
if !ok {
err = errors.New("未转换")
}
return
}
func transformation(sourceType, targetType reflect.Type, sourceValue, targetValue reflect.Value, cover bool) (isSet bool) {
for sourceType.Kind() == reflect.Ptr {
sourceType = sourceType.Elem()
}
for targetType.Kind() == reflect.Ptr {
targetType = targetType.Elem()
}
for sourceValue.Kind() == reflect.Ptr {
sourceValue = sourceValue.Elem()
}
for targetValue.Kind() == reflect.Ptr {
targetValue = targetValue.Elem()
}
// 获取目标字段,只获取不为0,且可设置的字段
var targetMap = make(map[string]int, targetValue.NumField())
for i := 0; i < targetValue.NumField(); i++ {
if !targetValue.Field(i).CanSet() {
continue
}
if !cover && !isBlank(targetValue.Field(i)) {
continue
}
targetMap[getFieldName(targetType.Field(i))] = i
}
if len(targetMap) == 0 {
return
}
// 获取源数据字段
var sourceMap = make(map[string]int, sourceType.NumField())
for i := 0; i < sourceType.NumField(); i++ {
name := getFieldName(sourceType.Field(i))
if _, ok := targetMap[name]; !ok {
continue
}
sourceMap[getFieldName(sourceType.Field(i))] = i
}
for name, source := range sourceMap {
target, ok := targetMap[name]
if !ok {
continue
}
// 不支持往下层走,后面再补充
if sourceValue.Field(source).Kind() == reflect.Ptr || sourceValue.Field(source).Kind() == reflect.Struct {
ok = transformation(sourceType.Field(source).Type, targetType.Field(target).Type, sourceValue.Field(source), targetValue.Field(target), cover)
if ok {
if !isSet {
isSet = true
}
continue
}
}
if sourceType.Field(source).Type != targetType.Field(target).Type {
continue
}
targetValue.Field(target).Set(sourceValue.Field(source))
isSet = true
}
return
}
// 获取字段tag或者name
func getFieldName(t reflect.StructField) string {
s := t.Tag.Get("gorm")
if s != "" {
s1 := strings.Split(s, ";")
for i := range s1 {
if strings.HasPrefix(s1[i], "column:") {
s = strings.TrimSpace(s1[i][6:])
break
}
}
if s != "" {
return s
}
}
s = t.Tag.Get("json")
if s != "" {
return s
}
return t.Name
}
// 判断反射的值是否为0
func isBlank(value reflect.Value) bool {
switch value.Kind() {
case reflect.String:
return value.Len() == 0
case reflect.Bool:
return !value.Bool()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return value.Int() == 0
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return value.Uint() == 0
case reflect.Float32, reflect.Float64:
return value.Float() == 0
case reflect.Interface, reflect.Ptr:
return value.IsNil()
}
return reflect.DeepEqual(value.Interface(), reflect.Zero(value.Type()).Interface())
}

浙公网安备 33010602011771号