在 Go 中实现 struct-to-csv 编写器

Posted

技术标签:

【中文标题】在 Go 中实现 struct-to-csv 编写器【英文标题】:Implement a struct-to-csv writer in Go 【发布时间】:2022-01-03 20:41:18 【问题描述】:

以下代码尝试为任何简单结构实现通用 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 所述,以及许多其他在线资源。

也就是说,是否可以使用 reflection 重写 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")
    
    ... ...

【问题讨论】:

pkg.go.dev/github.com/jszwec/csvutil#readme-marshal 【参考方案1】:

是否可以使用反射重写 CSVOutput() 函数

是的

// 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

【讨论】:

你能解释一下(*CSV)(nil)是什么吗?将 nil 转换为 CSV 界面?我们可以做到CSV(nil) 吗? "what is (*CSV)(nil)" -- 这是一个将nil 转换为*CSV 的表达式。 “我们能做到吗CSV(nil)——不行。您需要指向接口的指针,否则接口类型信息会丢失。 @xrfang 更完整的解释见:***.com/a/63938859/965900

以上是关于在 Go 中实现 struct-to-csv 编写器的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Go 中实现 PHP 函数 `die()`(或 `exit()`)?

是否可以将接口的参数类型留给在 Go 中实现它的接收器?

golang 来自Scratch的容器 - 在Go中实现简单的容器化应用程序

在 C++11 中实现 boost::barrier

如何在Golang中实现正确的并行性? goroutines是否与Go1.5 +并行?

编写简单的CLI程序:Python vs Go