如何用马提尼输出CSV?

时间:2015-03-21 13:15:58

标签: go export-to-csv martini

我想用马提尼打印CSV数据到输出。目前,我始终使用r.JSON(200, somestruct) r来自render.Render的{​​{1}}。

现在我有一块结构,我想将它们打印为CSV(将一个结构的每个字段字符串化并在一行打印一个结构)。

目前,我这样做:

github.com/martini-contrib

但我不喜欢我这样做的原因如下:

  • 直接下载,不会打印到屏幕上。
  • 我得到r.Data(200, []byte("id,Latitude,Longitude\n")) for _, packet := range tour.Packets { r.Data(200, []byte(strconv.FormatInt(packet.Id, 10)+","+strconv.FormatFloat(packet.Latitude, 'f', 6, 64)+","+strconv.FormatFloat(packet.Longitude, 'f', 6, 64)+"\n")) }
  • 我不想手动制作(结构包含更多字段,但所有字段都是http: multiple response.WriteHeader callsìnt64float64

如何以更简单的方式实施CSV导出选项?

1 个答案:

答案 0 :(得分:-1)

使用标准库。没有反射就没有通用的解决方案,但你可以简化它。

func handler(rw http.ResponseWriter) {
    rw.Header().Add("Content-Type", "text/csv")
    wr := csv.NewWriter(rw)
    err := wr.Write([]string{"id", "Latitude", "Longitude"})
    if err != nil {
        ...
    }
    for _, packet := range tour.Packets {
        err := wr.Write([]string{
            strconv.FormatInt(packet.Id, 10),
            strconv.FormatFloat(packet.Latitude, 'f', 6, 64),
            strconv.FormatFloat(packet.Longitude, 'f', 6, 64),
        })
        if err != nil {
            ...
        }
    }
}

如果您需要任何结构的通用解决方案,则需要反映。 见here

// structToStringSlice takes a struct value and
// creates a string slice of all the values in that struct
func structToStringSlice(i interface{}) []string {
    v := reflect.ValueOf(i)
    n := v.NumField()
    out := make([]string, n)
    for i := 0; i < n; i++ {
        field := v.Field(i)
        switch field.Kind() {
        case reflect.String:
            out[i] = field.String()
        case reflect.Int:
            out[i] = strconv.FormatInt(field.Int(), 10)
        // add cases here to support more field types.
        }
    }
    return out
}

// writeToCSV prints a slice of structs as csv to a writer
func writeToCSV(w io.Writer, i interface{}) {
    wr := csv.NewWriter(w)
    v := reflect.ValueOf(i)

    // Get slice's element type (some unknown struct type)
    typ := v.Type().Elem()
    numFields := typ.NumField()
    fieldSet := make([]string, numFields)
    for i := 0; i < numFields; i++ {
        fieldSet[i] = typ.Field(i).Name
    }
    // Write header row
    wr.Write(fieldSet)

    // Write data rows
    sliceLen := v.Len()
    for i := 0; i < sliceLen; i++ {
        wr.Write(structToStringSlice(v.Index(i).Interface()))
    }
    wr.Flush()
}

那么你的例子就是:

func handler(rw http.ResponseWriter) {
    ....
    writeToCSV(rw, tour.Packets)
}

我写的函数只适用于int或string字段。通过在structToStringSlice中向交换机添加案例,您可以轻松地将其扩展到更多类型。有关另一个Kinds的反映文档,请参见here