Go 結合反射輕鬆將結構體轉換成 Excel

Excel 中的一些概念

  1. 一個 excel 文件中可以包含多個sheet,一個sheet可以理解成一個表格

  2. 表格的每一行稱爲 Row

  3. 表格的每一行中的任意一個單元格稱爲 Cell

使用tealeg操作 Excel

安裝tealeg

go get github.com/tealeg/xlsx

使用tealeg新建一個表格

tealeg 提供了傻瓜式 API,主要流程爲創建一個 Sheet,在 Sheet 中添加 Row,然後在 Row 中添加每個單元格的值,最終持久化到磁盤。

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的表格,內容和前面介紹的 excel 概念中使用的表格是一個。

Go 結合反射將結構體轉換成 Excel

  1. 大概思路是在 Go 的結構體中每個屬性打上一個excel標籤

  2. 利用反射獲取標籤中的內容,作爲表格的 Header

  3. 利用反射獲取 Go 結構體中的屬性的值,組成一個 map,key 爲從標籤中excel反射獲取的值,val 爲結構體屬性具體的值,map 的僞代碼如下:map[excelTag]strucVal 類型爲map[string]string

  4. 如果一個 array 或者 slice 中的結構體需要轉換成 excel,那麼只需要將每個元素轉換成第 3 步提到的 map 作爲一行,組成一個[]map[excelTag]strucVal然後遍歷這個切片,追加到表格中即可。

反射獲取每個 Struct 中的 Tag

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

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
}

利用反射將一個 Silce,Array 或者 Struct 轉換成[]map[excelTag]strucVal

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

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
}

運行測試用例驗證

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 的結果

轉自:

https://juejin.cn/post/7106508948299055112

Go 開發大全

參與維護一個非常全面的 Go 開源技術資源庫。日常分享 Go, 雲原生、k8s、Docker 和微服務方面的技術文章和行業動態。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/b84Q20xsTm9gV4JTWriOwg