在Go语言中实现一个将结构体写入CSV文件的功能。

huangapple go评论82阅读模式
英文:

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 // Employee结构体实现了CSV接口
CSVOutput(w, data, nil)

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

我理解[]CSV[]Employee不同,可以在这里和其他在线资源中找到解释。

话虽如此,是否有可能通过使用反射来重写CSVOutput()函数:

func CSVOutput(w io.Writer, data interface{}, cols []string) error {
    sliceOfIntf := castToSlice(data) //如何实现这一步?
    if !implementedCSV(sliceOfIntf[0]) { //如何实现这一步?
        return errors.New("not csv")
    }
    ... ...
}
英文:

The following code attempt to implement a generic CSV writer for any simple struct. By "simple", I mean field value of the struct are of standard, simple types (int, string etc).

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
}

The problem is CSVOutput() does not actually work. e.g.:

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

Compilation failed: cannot use data (type []Employee) as type CSVArray in argument to CSVOutput

I understand that []CSV is not same as []Employee, as explained here, and many other resources available online.

That said, is it possible to rewrite the CSVOutput() function by using reflection:

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")
    }
    ... ...
}

答案1

得分: 2

是的,可以使用反射来重写CSVOutput()函数。

// 如果data是[]Employee{...},那么你可以这样做:

rv := reflect.ValueOf(data)
if rv.Kind() != reflect.Slice {
	return fmt.Errorf("data不是切片")
}
if !rv.Type().Elem().Implements(reflect.TypeOf((*CSV)(nil)).Elem()) {
	return fmt.Errorf("切片元素未实现CSV接口")
}

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

// 现在csvArr是包含data所有元素的CSVArray

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

英文:

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

Yes

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

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

csvArr := make(CSVArray, rv.Len())
for i := 0; i &lt; 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

huangapple
  • 本文由 发表于 2021年11月25日 23:03:39
  • 转载请务必保留本文链接:https://go.coder-hub.com/70113347.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定