【Golang】反射
829 字
4 分钟
【Golang】反射

0x00
程序正常编译时变量会被转换为内存地址,而变量名、类型信息等不会被编译进可执行部分。而使用反射(golang中使用reflect包)则会将这些信息编译进可执行文件(所以有时为了程序安全而不会使用反射)。
0x01 实例
贴上一个经常用的xlsx文件转[]*struct代码
import ( "errors" "github.com/tealeg/xlsx" "io" "net/http" "reflect")// ParseXlsxToStruct//// @Description: 解析xlsx到struct中// @param data xlsx数据// @param out 目标struct// @param sheet 默认第一个sheet// @return error// ParseXlsxToStruct(data,&out)func ParseXlsxToStruct(data []byte, out any, sheet ...string) error { value := reflect.ValueOf(out) if value.Type().Kind() != reflect.Ptr { return errors.New("必须以指针的方式传入") } value = value.Elem() if value.Type().Kind() != reflect.Slice { return errors.New("内部必须为struct切片") } if value.Len() != 0 { return errors.New("必须是空切片") }
xfile, err := xlsx.OpenBinary(data) if err != nil { return err } if len(xfile.Sheets) == 0 { return errors.New("xlsx中没有sheet") } var sheets []*xlsx.Sheet if len(sheet) > 0 { sheets = make([]*xlsx.Sheet, 0, len(sheet)) for _, v := range sheet { if s, ok := xfile.Sheet[v]; ok { sheets = append(sheets, s) } } } else { sheets = append(sheets, xfile.Sheets[0]) }
title := GetUniqueSheetsTitle(sheets) var tmp = make(map[string][]string, len(title)) for _, s := range sheets { if len(s.Rows) == 0 { continue } title := GetSheetTitle(s) for i, row := range s.Rows { if i == 0 { continue } var dataMap = make(map[string]string) for j, cell := range row.Cells { if title[j] == "" { continue } dataMap[title[j]] = cell.String() } for _, t := range title { if t == "" { continue } tmp[t] = append(tmp[t], dataMap[t]) } } } MapToStructByTag(tmp, out, "xlsx") return nil}
func MapToStructByTag(data map[string][]string, in interface{}, tagKey string) { // 获取传入结构体切片的反射类型和值 inValue := reflect.ValueOf(in).Elem() inType := inValue.Type().Elem()
var l int for _, v := range data { if l < len(v) { l = len(v) } } //fmt.Println(inType) news := reflect.MakeSlice(inValue.Type(), l, l) defer func() { inValue.Set(news) }()
for i := 0; i < news.Len(); i++ { structValue := news.Index(i) structType := inType // 获取结构体指针所指向的类型 var prt bool var structValuePtr reflect.Value if structValue.Kind() == reflect.Ptr { structType = structType.Elem() structValuePtr = reflect.New(structType) structValue = structValuePtr.Elem() prt = true }
// 遍历结构体字段 for j := 0; j < structType.NumField(); j++ { fieldValue := structValue.Field(j) fieldType := structType.Field(j) // 检查字段的 tag 是否与 key 匹配 tag := fieldType.Tag.Get(tagKey) if values, ok := data[tag]; ok { if i >= len(values) { continue } // 检查字段是否可以被设置 if fieldValue.CanSet() { // 根据字段类型进行适当的转换并设置值 switch fieldType.Type.Kind() { case reflect.String: structValue.Field(j).SetString(values[i]) } } }
} if prt { news.Index(i).Set(structValuePtr) } }}
func ParseXlsxToStructByUrl(url string, out any, sheet ...string) error { resp, err := http.Get(url) if err != nil { return err } defer resp.Body.Close() data, _ := io.ReadAll(resp.Body) return ParseXlsxToStruct(data, out, sheet...)}
func ParseStructToXlsx(a any, fn func(tag string, val any, cell *xlsx.Cell) error) (*xlsx.File, error) { val := reflect.ValueOf(a) if val.Type().Kind() != reflect.Slice { return nil, errors.New("必须传入[]*struct") } xFile := xlsx.NewFile() sheet, _ := xFile.AddSheet("A1") title := sheet.AddRow() for i := 0; i < val.Len(); i++ { elemVal := val.Index(i) for elemVal.Kind() == reflect.Ptr { elemVal = elemVal.Elem() } if elemVal.Kind() != reflect.Struct { return nil, errors.New("必须传入[]*struct") } row := sheet.AddRow() for j := 0; j < elemVal.NumField(); j++ { cell := row.AddCell() tag, ok := elemVal.Type().Field(j).Tag.Lookup("xlsx") if !ok { continue } if i == 0 { title.AddCell().SetString(tag) } v := elemVal.Field(j).Interface() if err := fn(tag, v, cell); err != nil { return nil, err } } } return xFile, nil}
func GetUniqueSheetsTitle(ss []*xlsx.Sheet) []string { var tmp = make(map[string]struct{}) var out []string for _, s := range ss { title := GetSheetTitle(s) for _, t := range title { if _, ok := tmp[t]; !ok && t != "" { out = append(out, t) } } } return out}
func GetSheetTitle(s *xlsx.Sheet) []string { if len(s.Rows) == 0 { return nil } if row := s.Rows[0]; row != nil { var title = make([]string, len(row.Cells)) for i, cell := range row.Cells { title[i] = cell.String() } return title } return nil}支持与分享
如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!
相关文章智能推荐
1
[Golang]ssh登录及执行命令
Golang2019-11-04
2
【Golang】使用反射实现验证struct必填字段是否为空
Golang笔记2021-08-20
3
【Golang】GO项目结构
Golang笔记2019-06-25
4
【Golang】使用CGO交叉编译
Golang笔记2020-04-08
5
【Golang】命令行传递参数
Golang笔记2018-09-13
随机文章随机推荐















加载更多…