You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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

火山引擎 最新活动