视频加载失败

【Golang】反射

829 字
4 分钟
【Golang】反射
【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
}

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!

打赏
【Golang】反射
https://rehtt.com/posts/263/
作者
Rehtt
发布于
2023-06-14
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
Rehtt
Hello, I'm Rehtt.
公告
欢迎来到我的博客!
分类
标签
最新动态
站点统计
文章
150
分类
17
标签
73
总字数
67,772
运行时长
0 天
最后活动
0 天前
站点信息
构建平台
Local
博客版本
Firefly v6.16.8
文章许可
CC BY-NC-SA 4.0
文章目录