在 Go 中实现一个 struct-to-csv writer

Implement a struct-to-csv writer in Go

以下代码尝试为任何简单结构实现通用 CSV 编写器。 “简单”是指结构的字段值是标准的、简单的类型(int、string 等)。

type (
    CSV interface {
        Header() []string
        String([]string) (string, error)
    }
    CSVArray []CSV
)


func CSVOutput(w io.Writer, data CSVArray, cols []string) error {
    if len(data) == 0 {
        return nil
    }
    _, err := fmt.Fprintln(w, data[0].Header())
    if err != nil {
        return err
    }
    for _, d := range data {
        str, err := d.String(cols)
        if err != nil {
            return err
        }
        _, err = fmt.Fprintln(w, str)
        if err != nil {
            return err
        }
    }
    return nil
}

问题是 CSVOutput() 实际上不起作用。例如:

var data []Employee //the Employee struct implements CSV interface
CSVOutput(w, data, nil)

编译失败:cannot use data (type []Employee) as type CSVArray in argument to CSVOutput

我了解 []CSV 与 []Employee 不同,如所解释的 here 以及许多其他在线资源。

也就是说,是否可以使用反射重写CSVOutput()函数:

func CSVOutput(w io.Writer, data interfac{}, cols []string) error {
    sliceOfIntf = castToSlice(data) //how to do this?
    if !implementedCSV(sliceOfIntf[0]) { //and how to do this?
        return errors.New("not csv")
    }
    ... ...
}

is it possible to rewrite the CSVOutput() function by using reflection

// if data is []Employee{...}, then you can do the following:

rv := reflect.ValueOf(data)
if rv.Kind() != reflect.Slice {
    return fmt.Errorf("data is not slice")
}
if !rv.Type().Elem().Implements(reflect.TypeOf((*CSV)(nil)).Elem()) {
    return fmt.Errorf("slice element does not implement CSV")
}

csvArr := make(CSVArray, rv.Len())
for i := 0; i < rv.Len(); i++ {
    csvArr[i] = rv.Index(i).Interface().(CSV)
}

// now csvArr is CSVArray containing all the elements of data

https://go.dev/play/p/gcSOid533gx