Unmarshal CSV record into struct in Go

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

Unmarshal CSV record into struct in Go

问题

问题是如何将CSV文件中的记录自动反序列化/解组为Go结构体。

例如,我有以下结构体:

type Test struct {
  Name string
  Surname string
  Age int
}

CSV文件包含以下记录:

John;Smith;42
Piter;Abel;50

是否有一种简单的方法将这些记录解组到结构体中,而不是使用"encoding/csv"包读取记录,然后执行类似以下的操作:

record, _ := reader.Read()
test := Test{record[0],record[1],atoi(record[2])}
英文:

The problem how to automatically deserialize/unmarshal record from CSV file into Go struct.

For example, I have

type Test struct {
  Name string
  Surname string
  Age int
}

And CSV file contains records

John;Smith;42
Piter;Abel;50

Is there an easy way to unmarshal those records into struct except by using "encoding/csv" package for reading record and then doing something like

record, _ := reader.Read()
test := Test{record[0],record[1],atoi(record[2])}

答案1

得分: 40

这是一个示例代码,使用gocarina/gocsv库来处理自定义结构,类似于encoding/json的方式。
你还可以为特定类型编写自定义的编组器和解组器。

示例代码如下:

type Client struct {
    Id   string `csv:"client_id"`   // .csv列标题
    Name string `csv:"client_name"`
    Age  string `csv:"client_age"`
}

func main() {
    in, err := os.Open("clients.csv")
    if err != nil {
        panic(err)
    }
    defer in.Close()

    clients := []*Client{}

    if err := gocsv.UnmarshalFile(in, &clients); err != nil {
        panic(err)
    }
    for _, client := range clients {
        fmt.Println("Hello, ", client.Name)
    }
}

这段代码使用gocsv.UnmarshalFile函数将CSV文件解组到clients切片中,并打印每个客户的姓名。

英文:

There is gocarina/gocsv which handles custom struct in the same way encoding/json does.
You can also write custom marshaller and unmarshaller for specific types.

Example:

type Client struct {
	Id      string `csv:"client_id"` // .csv column headers
	Name    string `csv:"client_name"`
	Age     string `csv:"client_age"`
}

func main() {
	in, err := os.Open("clients.csv")
	if err != nil {
		panic(err)
	}
	defer in.Close()

	clients := []*Client{}

   	if err := gocsv.UnmarshalFile(in, &clients); err != nil {
		panic(err)
	}
	for _, client := range clients {
		fmt.Println("Hello, ", client.Name)
	}
}

答案2

得分: 15

似乎我已经完成了将CSV记录自动编组为结构体(仅限于字符串和整数)的工作。希望这对你有用。

这是一个链接到playground的示例:http://play.golang.org/p/kwc32A5mJf

func Unmarshal(reader *csv.Reader, v interface{}) error {
    record, err := reader.Read()
    if err != nil {
        return err
    }
    s := reflect.ValueOf(v).Elem()
    if s.NumField() != len(record) {
        return &FieldMismatch{s.NumField(), len(record)}
    }
    for i := 0; i < s.NumField(); i++ {
        f := s.Field(i)
        switch f.Type().String() {
        case "string":
            f.SetString(record[i])
        case "int":
            ival, err := strconv.ParseInt(record[i], 10, 0)
            if err != nil {
                return err
            }
            f.SetInt(ival)
        default:
            return &UnsupportedType{f.Type().String()}
        }
    }
    return nil
}

如果有人需要这个实现,我会尝试创建一个GitHub包。

英文:

Seems I've done with automatic marshaling of CSV records into structs (limited to string and int). Hope this would be useful.

Here is a link to playground: http://play.golang.org/p/kwc32A5mJf

func Unmarshal(reader *csv.Reader, v interface{}) error {
	record, err := reader.Read()
	if err != nil {
		return err
	}
	s := reflect.ValueOf(v).Elem()
	if s.NumField() != len(record) {
		return &amp;FieldMismatch{s.NumField(), len(record)}
	}
	for i := 0; i &lt; s.NumField(); i++ {
		f := s.Field(i)
		switch f.Type().String() {
		case &quot;string&quot;:
			f.SetString(record[i])
		case &quot;int&quot;:
			ival, err := strconv.ParseInt(record[i], 10, 0)
			if err != nil {
				return err
			}
			f.SetInt(ival)
		default:
			return &amp;UnsupportedType{f.Type().String()}
		}
	}
	return nil
}

I'll try to create github package is someone needs this implementation.

答案3

得分: 1

你可以自己编写代码。也许可以像这样:

package main

import (
	"fmt"
	"strconv"
	"strings"
)

type Test struct {
	Name    string
	Surname string
	Age     int
}

func (t Test) String() string {
	return fmt.Sprintf("%s;%s;%d", t.Name, t.Surname, t.Age)
}

func (t *Test) Parse(in string) {
	tmp := strings.Split(in, ";")
	t.Name = tmp[0]
	t.Surname = tmp[1]
	t.Age, _ = strconv.Atoi(tmp[2])
}

func main() {

	john := Test{"John", "Smith", 42}
	fmt.Printf("john:%v\n", john)

	johnString := john.String()
	fmt.Printf("johnString:%s\n", johnString)

	var rebornJohn Test
	rebornJohn.Parse(johnString)
	fmt.Printf("rebornJohn:%v\n", rebornJohn)

}

这段代码定义了一个名为Test的结构体,包含NameSurnameAge三个字段。它还定义了两个方法:String()用于将结构体转换为字符串,Parse()用于将字符串解析为结构体。在main()函数中,创建了一个john对象,并对其进行打印输出。然后,将john对象转换为字符串并打印输出。接下来,创建了一个新的rebornJohn对象,并使用Parse()方法将字符串解析为结构体,并对其进行打印输出。

英文:

You could bake your own. Perhaps something like this:

package main

import (
	&quot;fmt&quot;
	&quot;strconv&quot;
	&quot;strings&quot;
)

type Test struct {
	Name    string
	Surname string
	Age     int
}

func (t Test) String() string {
	return fmt.Sprintf(&quot;%s;%s;%d&quot;, t.Name, t.Surname, t.Age)
}

func (t *Test) Parse(in string) {
	tmp := strings.Split(in, &quot;;&quot;)
	t.Name = tmp[0]
	t.Surname = tmp[1]
	t.Age, _ = strconv.Atoi(tmp[2])
}

func main() {

	john := Test{&quot;John&quot;, &quot;Smith&quot;, 42}
	fmt.Printf(&quot;john:%v\n&quot;, john)

	johnString := john.String()
	fmt.Printf(&quot;johnString:%s\n&quot;, johnString)

	var rebornJohn Test
	rebornJohn.Parse(johnString)
	fmt.Printf(&quot;rebornJohn:%v\n&quot;, rebornJohn)

}

答案4

得分: 1

使用csvutil可以给列标题,参见示例

在您的情况下,可以这样做:

package main

import (
	"encoding/csv"
	"fmt"
	"io"
	"os"

	"github.com/jszwec/csvutil"
)

type Test struct {
	Name    string
	Surname string
	Age     int
}

func main() {
	csv_file, _ := os.Open("test.csv")
	reader := csv.NewReader(csv_file)
	reader.Comma = ';'

	userHeader, _ := csvutil.Header(Test{}, "csv")
	dec, _ := csvutil.NewDecoder(reader, userHeader...)

	var users []Test
	for {
		var u Test
		if err := dec.Decode(&u); err == io.EOF {
			break
		}
		users = append(users, u)
	}

	fmt.Println(users)
}
英文:

Using csvutil it is possible to give column header see example.

In your case, this could be :

package main

import (
	&quot;encoding/csv&quot;
	&quot;fmt&quot;
	&quot;io&quot;
	&quot;os&quot;

	&quot;github.com/jszwec/csvutil&quot;
)

type Test struct {
	Name    string
	Surname string
	Age     int
}

func main() {
	csv_file, _ := os.Open(&quot;test.csv&quot;)
	reader := csv.NewReader(csv_file)
	reader.Comma = &#39;;&#39;

	userHeader, _ := csvutil.Header(Test{}, &quot;csv&quot;)
	dec, _ := csvutil.NewDecoder(reader, userHeader...)

	var users []Test
	for {
		var u Test
		if err := dec.Decode(&amp;u); err == io.EOF {
			break
		}
		users = append(users, u)
	}

	fmt.Println(users)
}

答案5

得分: 0

一种简单的解决方法是使用JSON作为中间表示。

一旦你这样做了,你就有各种工具可供使用。

你可以...

  • 直接解组到你的类型(如果它全是字符串)
  • 解组到map[string]interface{},然后进行必要的类型转换
  • 解组 -> 转换类型 -> 重新组合JSON -> 解组到你的类型

下面是一个简单的通用组合函数,可以实现这个流程...

pairToJSON := func(header, record []string) string {
	raw := ""
	for j, v := range record {
		if j != 0 {
			raw += ",\n"
		}
		raw += "\"" + header[j] + "\":\"" + v + "\""
	}
	raw = "{\n" + raw + "\n}"
	return raw
}

上述代码与标准csv库生成的[]string数据兼容。

英文:

A simple way to solve the problem is to use JSON as an intermediate representation.

Once you've done this, you have a variety of tools at your disposal.

You can...

  • Unmarshal directly to your type (if it's all strings)
  • Unmarshal to a map[string]interface{} and then make any necessary type conversions
  • Unmarshal -> convert types -> remarshal JSON -> unmarshal to your type

Here's a simple generic marshalling function that enables this flow...

pairToJSON := func(header, record []string) string {
	raw := &quot;&quot;
	for j, v := range record {
		if j != 0 {
			raw += &quot;,\n&quot;
		}
		raw += &quot;\&quot;&quot; + header[j] + &quot;\&quot;:\&quot;&quot; + v + &quot;\&quot;&quot;
	}
	raw = &quot;{\n&quot; + raw + &quot;\n}&quot;
	return raw
}

The above is compatible with the []string data produced by the standard csv library.

huangapple
  • 本文由 发表于 2013年12月25日 10:22:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/20768511.html
匿名

发表评论

匿名网友

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

确定