Golang如何将Excel工作表转换为自定义结构体数组?
将Excel数据映射为自定义结构体数组(基于Excelize)
你已经搞定了Excel的基础读取,现在要把数据转成结构体数组的话,咱们可以自己封装一个便捷的通用方法,完全能实现你想要的类似READMETHOD的调用方式。下面是具体的实现方案:
第一步:修正结构体定义
首先要注意,结构体的字段必须首字母大写——因为Go的反射机制只能访问导出的字段(首字母大写),否则无法完成赋值。调整后的结构体:
type InvoiceSheet struct { Amount string // 对应Excel中的"Amount"表头 Item string // 对应Excel中的"Item"表头 }
第二步:封装通用读取函数
我们可以写一个通用函数,接收文件路径、工作表名,以及结构体切片的指针,自动完成Excel到结构体的映射:
import ( "fmt" "reflect" "github.com/360EntSecGroup-Skylar/excelize" ) // ReadExcelToStruct 读取Excel数据并映射到结构体切片 // 参数:filePath Excel文件路径,sheetName 工作表名,target 结构体切片的指针(比如&[]InvoiceSheet{}) func ReadExcelToStruct(filePath, sheetName string, target interface{}) error { // 验证target是否为结构体切片的指针 targetType := reflect.TypeOf(target) if targetType.Kind() != reflect.Ptr || targetType.Elem().Kind() != reflect.Slice { return fmt.Errorf("target must be a pointer to a slice of struct") } // 打开Excel文件 f, err := excelize.OpenFile(filePath) if err != nil { return err } // 获取工作表所有行 rows, err := f.GetRows(sheetName) if err != nil { return err } if len(rows) < 2 { // 至少需要表头+一行数据 return fmt.Errorf("no data found in sheet %s", sheetName) } // 获取表头行(第一行) headers := rows[0] // 获取结构体的类型(切片元素的类型) elemType := targetType.Elem().Elem() // 获取target的反射值 targetVal := reflect.ValueOf(target).Elem() // 遍历数据行(从第二行开始) for i := 1; i < len(rows); i++ { row := rows[i] // 创建一个新的结构体实例 elemVal := reflect.New(elemType).Elem() // 遍历表头,匹配结构体字段并赋值 for j, header := range headers { // 根据表头名称查找结构体字段 field := elemVal.FieldByName(header) if field.IsValid() && field.CanSet() && j < len(row) { field.SetString(row[j]) } } // 将结构体实例添加到切片中 targetVal.Set(reflect.Append(targetVal, elemVal)) } return nil }
第三步:使用示例
现在你就可以像你期望的那样调用这个函数了:
func main() { var invoices []InvoiceSheet err := ReadExcelToStruct("Book1.xlsx", "Sheet1", &invoices) if err != nil { fmt.Println(err) return } // 遍历结构体数组 for _, inv := range invoices { fmt.Printf("Amount: %s, Item: %s\n", inv.Amount, inv.Item) } }
进阶优化:支持字段标签
如果你的Excel表头和结构体字段名不一致(比如表头是"金额",结构体字段是"Amount"),可以给结构体字段添加标签来映射:
type InvoiceSheet struct { Amount string `excel:"金额"` Item string `excel:"商品"` }
然后修改ReadExcelToStruct函数中查找字段的逻辑,优先使用标签值:
// 替换原来的field查找逻辑 field := elemVal.FieldByName(header) if !field.IsValid() { // 尝试通过标签查找 for k := 0; k < elemType.NumField(); k++ { structField := elemType.Field(k) tag := structField.Tag.Get("excel") if tag == header { field = elemVal.Field(k) break } } } if field.IsValid() && field.CanSet() && j < len(row) { field.SetString(row[j]) }
这样就能更灵活地处理表头和字段名不匹配的情况啦!
内容的提问来源于stack exchange,提问作者Abanoub Istfanous




