字节笔记本

2026年2月22日

Go 结合反射轻松将结构体转换成 Excel

本文介绍如何使用 Go 语言的反射机制结合 tealeg/xlsx 库,轻松将结构体数据转换为 Excel 表格。通过标签(tag)定义表头,利用反射自动提取字段值,实现通用的结构体转 Excel 解决方案。

Excel 中的一些概念

在开始之前,先了解一些 Excel 的基本概念:

  • 一个 excel 文件中可以包含多个 sheet,一个 sheet 可以理解成一个表格
  • 表格的每一行称为 Row
  • 表格的每一行中的任意一个单元格称为 Cell

使用 tealeg 操作 Excel

安装 tealeg

bash
go get github.com/tealeg/xlsx

使用 tealeg 新建一个表格

tealeg 提供了傻瓜式 API,主要流程为创建一个 Sheet,在 Sheet 中添加 Row,然后在 Row 中添加每个单元格的值,最终持久化到磁盘。

go
func TestTealeg(t *testing.T) {
    excel := xlsx.NewFile()

    // 新建一个 sheet
    sheet, err := excel.AddSheet("Sheet1")
    if err != nil {
        t.Fatal(err)
    }

    // 创建首行
    headerRow := sheet.AddRow()

    // 设置行高
    headerRow.SetHeightCM(0.5)

    // 填充行中的单元格
    headerRow.AddCell().Value = "姓名"
    headerRow.AddCell().Value = "年龄"

    valList := [][]string{
        {"张三", "13"},
        {"李四", "14"},
        {"王五", "15"},
    }

    for _, line := range valList {
        row := sheet.AddRow()
        row.SetHeightCM(0.5)
        for _, v := range line {
            row.AddCell().Value = v
        }
    }

    // 持久化到磁盘
    if err := excel.Save("username.xlsx"); err != nil {
        t.Fatal(err)
    }
}

执行这个 Test 用例后,在项目的当前文件夹中会出现一个 username.xlsx 的表格。

Go 结合反射将结构体转换成 Excel

实现思路

  • 在 Go 的结构体中每个属性打上一个 excel 标签
  • 利用反射获取标签中的内容,作为表格的 Header
  • 利用反射获取 Go 结构体中的属性的值,组成一个 map,key 为从标签中反射获取的值,val 为结构体属性具体的值
  • 如果一个 array 或者 slice 中的结构体需要转换成 excel,那么只需要将每个元素转换成 map 作为一行,组成一个 []map[excelTag]strucVal,然后遍历这个切片,追加到表格中即可

反射获取每个 Struct 中的 Tag

go
func getStructTagList(v interface{}, tag string) []string {
    var resList []string
    if v == nil {
        return resList
    }
    var item interface{}
    switch reflect.TypeOf(v).Kind() {
    case reflect.Slice, reflect.Array:
        values := reflect.ValueOf(v)
        if values.Len() == 0 {
            return resList
        }
        item = values.Index(0).Interface()
    case reflect.Struct:
        item = reflect.ValueOf(v).Interface()
    default:
        panic(fmt.Sprintf("type %v not support", reflect.TypeOf(v).Kind()))
    }
    typeOf := reflect.TypeOf(item)
    fieldNum := typeOf.NumField()
    for i := 0; i < fieldNum; i++ {
        resList = append(resList, typeOf.Field(i).Tag.Get(tag))
    }
    return resList
}

通过反射将结构体的值转换成 map[excelTag]strucVal

go
func getTagValMap(v interface{}, tag string) map[string]string {
    resMap := make(map[string]string)
    if v == nil {
        return resMap
    }
    typeOf := reflect.TypeOf(v)
    fieldNum := typeOf.NumField()
    for i := 0; i < fieldNum; i++ {
        structField := typeOf.Field(i)
        tagValue := structField.Tag.Get(tag)
        val := reflect.ValueOf(v).FieldByName(structField.Name)
        resMap[tagValue] = fmt.Sprintf("%v", val.Interface())
    }
    return resMap
}

利用反射将 Slice、Array 或 Struct 转换成 []map[excelTag]strucVal

go
func struct2MapTagList(v interface{}, tag string) []map[string]string {
    var resList []map[string]string
    switch reflect.TypeOf(v).Kind() {
    case reflect.Slice, reflect.Array:
        values := reflect.ValueOf(v)
        for i := 0; i < values.Len(); i++ {
            resList = append(resList, getTagValMap(values.Index(i).Interface(), tag))
        }
        break
    case reflect.Struct:
        val := reflect.ValueOf(v).Interface()
        resList = append(resList, getTagValMap(val, tag))
        break
    default:
        panic(fmt.Sprintf("type %v not support", reflect.TypeOf(v).Kind()))
    }
    return resList
}

通过 tealeg 将 []map[excelTag]strucVal 转换成 Excel

通过上面两步,已经可以将一个结构体、切片或者 list 转换成了一个 []map[excelTag]strucVal 类型的切片,下面我们只需要调用 tealeg 转换成 excel:

go
func Struct2Xlsx(v interface{}) (*xlsx.File, error) {
    var tag = "excel"
    tagList := getStructTagList(v, tag)
    mapTagList := struct2MapTagList(v, tag)
    excelFile := xlsx.NewFile()
    sheet, err := excelFile.AddSheet("Sheet1")
    if err != nil {
        return nil, err
    }
    headerRow := sheet.AddRow()
    for _, tagVal := range tagList {
        headerRow.SetHeightCM(0.5)
        headerRow.AddCell().Value = tagVal
    }
    for _, mapTagVal := range mapTagList {
        row := sheet.AddRow()
        for _, tagVal := range tagList {
            row.SetHeightCM(0.5)
            row.AddCell().Value = mapTagVal[tagVal]
        }
    }
    return excelFile, nil
}

运行测试用例验证

go
type Username struct {
    Name string `excel:"姓名"`
    Age  int    `excel:"年龄"`
}

func TestStruct2Excet(t *testing.T) {
    var data = []Username{
        {
            Name: "张三",
            Age:  13,
        },
        {
            Name: "李四",
            Age:  14,
        },
        {
            Name: "王五",
            Age:  15,
        },
    }
    excel, err := Struct2Xlsx(data)
    if err != nil {
        t.Fatal(err)
    }
    if err := excel.Save("username.xlsx"); err != nil {
        t.Fatal(err)
    }
}

测试运行成功后,会在项目目录创建一个 username.xlsx 的文件,这个文件就是我们的结构体转换成 excel 的结果。

总结

通过本文介绍的方法,你可以:

  1. 使用结构体标签定义 Excel 表头
  2. 利用 Go 反射自动提取结构体字段值
  3. 支持单个结构体、切片、数组等多种数据类型
  4. 通过 tealeg/xlsx 库生成标准的 Excel 文件

这种方式非常适合需要将数据库查询结果或 API 响应数据导出为 Excel 的场景,代码简洁且易于维护。

分享: