• 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
    "
    	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 {
    ", 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`
    ", 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`
    ", 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`
    ", field.Name, fieldType, field.Tag)
    			}
    
    		}
    	}
    	str += "}
    "
    	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里去了。一天天的,不知道在干啥~


    Shopee(虾皮)内推点击此处,岗位多多地,薪资高高地



    转载请注明出处


  • 相关阅读:
    30流的使用和分类
    使用EF Model First创建edmx模型,数据库有数据的情况下,如何同时更新模型和数据库
    29防止程序集被篡改仿冒,全局程序集缓存GAC
    报错:不允许保存更改。您所做的更改要求删除并重新创建以下表……
    28先判断是否存在,再创建文件夹或文件,递归计算文件夹大小
    27程序集资源
    MVC缓存02,使用数据层缓存,添加或修改时让缓存失效
    26复杂类型比较,使用Compare .NET objects组件
    25LINQ拾遗及实例
    MVC缓存01,使用控制器缓存或数据层缓存
  • 原文地址:https://www.cnblogs.com/zhangcaiwang/p/14965264.html
Copyright © 2020-2023  润新知