LoginSignup
1
0

More than 1 year has passed since last update.

【Go】構造体のスライスからCSV生成

Last updated at Posted at 2021-05-09

はじめに

GoでCSVの生成をしたいときに縛りが緩い汎用的な処理が欲しかったので、構造体のスライスを突っ込めば良いだけの処理を作成しました。

成果物

file.go
package file

import (
    "encoding/csv"
    "fmt"
    "os"
    "reflect"
    "strconv"
)

func GenerateCSV(i interface{}, fileName string) {
    reflectType := reflect.TypeOf(i)
    reflectTypeElem := reflectType.Elem()

    if reflectType.Kind() != reflect.Slice || reflectTypeElem.Kind() != reflect.Struct {
        fmt.Println("構造体のスライスのみ使えます")
        return
    }

    name := fmt.Sprintf("/tmp/%s.csv", fileName)
    f, err := os.Create(name)
    if err != nil {
        panic(err)
    }
    defer f.Close()

    writer := csv.NewWriter(f)
    writer.Write(buildHeader(reflectTypeElem))

    reflectValue := reflect.Indirect(reflect.ValueOf(i))
    for index := 0; index < reflectValue.Len(); index++ {
        record := reflectValue.Index(index)
        writer.Write(buildBody(record))
    }
    writer.Flush()

    fmt.Printf("CSVが生成されました。 %s\n", name)
}

func buildHeader(rt reflect.Type) []string {
    header := []string{}
    for i := 0; i < rt.NumField(); i++ {
        rs := rt.Field(i)
        header = append(header, rs.Tag.Get("json"))
    }
    return header
}

func buildBody(rv reflect.Value) []string {
    body := []string{}
    for i := 0; i < rv.Type().NumField(); i++ {
        v := rv.Field(i)

        switch v.Kind() {
        case reflect.Int:
            body = append(body, strconv.FormatInt(v.Int(), 10))
        case reflect.String:
            body = append(body, v.String())
        default:
            body = append(body, "") // 例外でもOK。お好みで。
        }
    }
    return body
}

使用例

main.go
type Hoge struct {
    ID   int    `json:"id"`
    Name string `json:"name"`
}
type HogeList []Hoge

func test() {
    hoges := HogeList{
        Hoge{
            ID:   1,
            Name: "name1",
        },
        Hoge{
            ID:   2,
            Name: "name2",
        },
    }

    fileName := "hoge"
    file.GenerateCSV(hoges, fileName)
}

実行結果

hoge.csv
id,name
1,name1
2,name2

感想

理想はGenerateCSVの引数型をinterfaceではなく縛ることで確認処理を辞めたかったのですが、気軽に使えることを優先してこのような形に落ち着きました。

reflect周りがもう少しスッキリできるかも?と思いつつ今回は一旦ここまでにしようかと思います。

参考

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0