Loading... # 0x00 程序正常编译时变量会被转换为内存地址,而变量名、类型信息等不会被编译进可执行部分。而使用`反射`(golang中使用`reflect`包)则会将这些信息编译进可执行文件(所以有时为了程序安全而不会使用反射)。 # 0x01 实例 贴上一个经常用的xlsx文件转[]*struct代码 ```golang 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<span class="label bg-light dk"></span>; 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 } ``` Last modification:June 14, 2023 © Allow specification reprint Support Appreciate the author AliPayWeChat Like 1 如果觉得我的文章对你有用,请随意赞赏