Go使用反射递归复制结构体
前言
go初学,今天本来有个需求涉及到用反射,然后花了时间边学边做,嗯,做完了之后发现做复杂了。缘由如下,有个结构体不能直接用,需要对里面的某个字段做一下调整,但是考虑到这个结构体里的其他字段会经常做一些变动,所以就想着使用反射自动化复制一份出来,并对需要调整的字段进行调整,以后再有增减字段,直接执行脚本就可以同步改动了。还是太天真,下面的例子是做了简化的例子,就瞎写,大家凑合看吧,我也不知道这个东西能有啥用~
代码结构

1. common里放的生成的结果。
2. scripts里的generate.go是实现,里面有一个main方法供脚本调用
3. scripts里的bash.sh是调用脚本。
4. struct.go里是要复制的结构体样本。
代码
struct.go
package reflectLearning
type Student struct {
Name string `json:"name"`
Age int64 `json:"age"`
Body *Body `json:"body"`
Bodys []*Body `json:"bodys"` // 这里单纯是为了多样化
}
type Body struct {
Height int64 `json:"height"`
Weight int64 `json:"weight"`
}
generate.go
package main
import (
"bufio"
"fmt"
"goLearning/reflectLearning"
"os"
"reflect"
"strings"
)
var (
voucherProtos = []reflect.Type{
reflect.TypeOf(reflectLearning.Student{}),
}
)
// 定义普通类型,非结构体
var baseTypes = map[reflect.Kind]bool{
reflect.Bool: true,
reflect.Int: true,
reflect.Int8: true,
reflect.Int16: true,
reflect.Int32: true,
reflect.Int64: true,
reflect.Uint: true,
reflect.Uint8: true,
reflect.Uint16: true,
reflect.Uint32: true,
reflect.Uint64: true,
reflect.Uintptr: true,
reflect.Float32: true,
reflect.Float64: true,
reflect.Map: true,
reflect.String: true,
}
func main() {
var voucherStructs = "package common\n"
for _, proto := range voucherProtos {
str := generateStructByType("", proto)
voucherStructs += str
}
file, err := os.Create("../../common/request.go")
if err != nil {
fmt.Println("文件打开失败", err)
}
//及时关闭file句柄
defer file.Close()
//写入文件时,使用带缓存的 *Writer
write := bufio.NewWriter(file)
write.WriteString(voucherStructs)
//Flush将缓存的文件真正写入到文件中
write.Flush()
}
// 递归生成结构体方法
func generateStructByType(content string, tp reflect.Type) string {
str := fmt.Sprintf("type %s struct {\n", tp.Name())
if i := strings.IndexAny(content, str); i != -1 { // 避免重复生成结构体
return ""
}
var voucherStructs string
// 遍历结构体的所有字段
for i := 0; i < tp.NumField(); i++ {
field := tp.Field(i)
if baseTypes[field.Type.Kind()] { // 基础类型
fieldType := field.Type.Name()
if field.Name == "VoucherID" {
fieldType = "string"
}
str += fmt.Sprintf("%s %s `%s`\n", field.Name, fieldType, field.Tag)
} else if field.Type.Kind() == reflect.Ptr { // 指针类型
pointType := field.Type.Elem() // 指针指向的类型
if !baseTypes[pointType.Kind()] { // 不是基础类型则认为是结构体,递归生成结构体
voucherStructs += generateStructByType(voucherStructs, pointType)
}
str += fmt.Sprintf("%s *%s `%s`\n", field.Name, pointType.Name(), field.Tag)
} else if field.Type.Kind() == reflect.Slice { // 数组,分为基本类型数组,结构体数组,指针基本类型数据,指针结构体数组
fieldType := "[]"
ele := field.Type.Elem()
// 先判断是不是指针数组
if ele.Kind() == reflect.Ptr {
fieldType += "*"
// 指针类型是基本类型还是结构体
subEle := ele.Elem()
if baseTypes[subEle.Kind()] { // 基本类型
fieldType += subEle.Name() // 结构体
} else {
fieldType += subEle.Name()
voucherStructs += generateStructByType(voucherStructs, subEle)
}
str += fmt.Sprintf("%s %s `%s`\n", field.Name, fieldType, field.Tag)
}
}
}
str += "}\n"
voucherStructs = voucherStructs + str
return voucherStructs
}
bash.sh
#!/C:/Program Files/Git/bin/bash.exe # 第一行是在win系统下才需要指定,mac系统不需要 go run -mod=vendor generate.go cd ../../common # 生成完代码后整理一下格式,不然不好看 gofmt -w ./
成品
request.go
package common
type Body struct {
Height int64 `json:"height"`
Weight int64 `json:"weight"`
}
type Student struct {
Name string `json:"name"`
Age *int64 `json:"age"`
Body *Body `json:"body"`
Bodys []*Body `json:"bodys"`
}
结语
改动Student结构后,点击执行bash.sh就能同步到request.go里去了。一天天的,不知道在干啥~

浙公网安备 33010602011771号