Go 语言 Excel 表格转成 Struct

发布时间 2023-12-19 15:13:17作者: 落樱纷飞

- 最近用go语言的Excelize包实现xlsx读取发现一点问题。当xlsx末尾列数据为空时(下图中红框部分),如果存入到像[]string切片或者数组内,Go语言数组这种会自动忽略右边的空值,会出现行的长度不一致(比如第14行列数为4,15行列数为3,第18行直接没数据)。这比python的pandas难用多了,现总结有几种方法:1.修改原数据(在列末尾添加一个全是一样特殊字符的结束列或者把空cell用特殊字符填充),好处是数据都是对齐,缺点是费时费力。2.代码调整(2.1用数组方式接收字段,接收后需要判断该字段是否空,空就用一个特殊字符替代,然后插入数据库内 2.2用反射的方式实现,用结构体存储,字段为空也不影响)。综合优缺点,开始用2.2方法实现

 

image

包的导入

import (
	"fmt"
	"github.com/xuri/excelize/v2"
	"reflect"
	"strconv"
	"strings"
)
//	定义结构体 TableColumn, “xlsx"是自定义标签
type TableColumn struct {
	Classification string `json:"classification" xlsx:"分类"`
	TagNumber      string `json:"tagNumber" xlsx:"标签序号"`
	FirstlyLabel   string `json:"firstlyLabel" xlsx:"一级标签"`
	SecondaryLabel string `json:"secondaryLabel" xlsx:"二级标签"`
	TertiaryLabel  string `json:"tertiaryLabel" xlsx:"三级标签"`
}
// 按行读取数据
func getRows(fileName string) [][]string {
	file, err := excelize.OpenFile(fileName)
	if err != nil {
		panic(err)
	}
	defer file.Close()
	rows, err := file.GetRows(file.GetSheetList()[0], excelize.Options{})
	if err != nil {
		panic(err)
	}
	return rows
}
// 初始化 标签序列 对应的 结构体字段 顺序
func initTag2FieldIdx(v interface{}, tagKey string) map[string]int {
	u := reflect.TypeOf(v)
	numField := u.NumField()
	tag2fieldIndex := map[string]int{}
	for i := 0; i < numField; i++ {
		f := u.Field(i)
		tagValue, ok := f.Tag.Lookup(tagKey)
		if ok {
			tag2fieldIndex[tagValue] = i
		} else {
			continue
		}
	}
	return tag2fieldIndex
}
// 将 行数据 按照反射的方式转为结构体 TableColumn
func rowsToTableColumns(rows [][]string, tag2fieldIndex map[string]int) []*TableColumn {
	var data []*TableColumn
	// 默认第一行对应tag
	head := rows[0]
	for _, row := range rows[1:] {
		tbCol := &TableColumn{}
		rv := reflect.ValueOf(tbCol).Elem()
		for i := 0; i < len(row); i++ {
			colCell := row[i]
			// 通过 tag 取到结构体字段下标
			fieldIndex, ok := tag2fieldIndex[head[i]]
			if !ok {
				continue
			}

			colCell = strings.Trim(colCell, " ")
			// 通过字段下标找到字段放射对象
			v := rv.Field(fieldIndex)
			// 根据字段的类型,选择适合的赋值方法
			switch v.Kind() {
			case reflect.String:
				value := colCell
				v.SetString(value)
			case reflect.Int64, reflect.Int32:
				value, err := strconv.Atoi(colCell)
				if err != nil {
					panic(err)
				}
				v.SetInt(int64(value))
			case reflect.Float64:
				value, err := strconv.ParseFloat(colCell, 64)
				if err != nil {
					panic(err)
				}
				v.SetFloat(value)
			}
		}

		data = append(data, tbCol)
	}
	return data
}
func main() {
	initTag2FieldIdx := initTag2FieldIdx(TableColumn{}, "xlsx")
	rows := getRows("excel/test.xlsx")
	tbCols := rowsToTableColumns(rows, initTag2FieldIdx)
	for _, s := range tbCols {
		fmt.Printf("%+v\n", s)
	}
}

结果:
image

参考:

1. 小试牛刀:Go 反射帮我把 Excel 转成 Struct - Zioyi - 博客园 (cnblogs.com)