Skip to content

Marshal/Unmarshal CSV data to/from struct slices

License

Notifications You must be signed in to change notification settings

j0hnsmith/csvplus

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

62 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

csvplus

GoDoc

csvplus provides marshalling/unmarshalling of CSV data (with and without header rows) into slices of structs.

Why?

csv.NewReader().Read() only provides records as []string leaving the user to perform type conversion. Also more convenient to go to/from a slice.

Examples

Unmarshal

type Item struct {
    First  string     `csvplus:"first"`
    Second int        `csvplus:"second"`
    Third  *bool      `csvplus:"third"`
    Forth  *time.Time `csvplus:"forth" csvplusFormat:"2006-01"`
}

// The CSV data we want to unmarshal.
// If your data is in a *File (or other io.Reader), use UnmarshalReader().
data := []byte("first,second,third,forth\na,1,,2000-01\nb,2,f,")

var items []Item
err := csvplus.Unmarshal(data, &items)
if err != nil {
    panic(err)
}

fmt.Printf("%+v\n", items[0])
fmt.Printf("{First:%s Second:%d Third:%t (dereferenced) Forth:%s}\n", items[1].First, items[1].Second, *items[1].Third, items[1].Forth)
// Output:
// {First:a Second:1 Third:<nil> Forth:2000-01-01 00:00:00 +0000 UTC}
// {First:b Second:2 Third:false (dereferenced) Forth:<nil>}

Custom field unmarshalling

// YesNoBool is an example field that implements Unmarshaler, it's used in an example.
type YesNoBool bool

// UnmarshalCSV is an implementation of the Unmarshaler interface, converts a string record to a native
// value for this type.
func (ynb *YesNoBool) UnmarshalCSV(s string) error {
    if ynb == nil {
        return fmt.Errorf("cannot unmarshal into nil pointer")
    }
    switch s {
    case "yes":
        *ynb = YesNoBool(true)
        return nil
    case "no":
        *ynb = YesNoBool(false)
        return nil
    }
    return fmt.Errorf("unable to convert %s to bool", s)
}

type Item struct {
    Name      string     `csvplus:"name"`
    Seen      *YesNoBool `csvplus:"seen"`   // custom type that implements Unmarshaler
    Agreed    YesNoBool  `csvplus:"agreed"` // custom type that implements Unmarshaler
    Timestamp *time.Time `csvplus:"when" csvplusFormat:"2006-01"`
}

// The CSV data we want to unmarshal, note the custom format.
data := []byte("name,seen,agreed,when\nRob,yes,yes,1999-11\nRuss,,no,")

var items []Item
err := csvplus.Unmarshal(data, &items)
if err != nil {
    panic(err)
}

for _, item := range items {
    fmt.Println(item)
}

Marshal

type Item struct {
    First  string     `csvplus:"first"`
    Second int        `csvplus:"second"`
    Third  *bool      `csvplus:"third"`
    Fourth *time.Time `csvplus:"fourth" csvplusFormat:"2006-01"`
}

tm, _ := time.Parse("2006-01", "2000-01")
f := false
items := []Item{
    {"a", 1, nil, &tm},
    {"b", 2, &f, nil},
}
data, err := csvplus.Marshal(&items)
if err != nil {
    panic(err)
}

fmt.Println(string(data))
// Output:
// first,second,third,fourth
// a,1,,2000-01
// b,2,false,

Ideas for improvement

  • csvplusNilVal tag for custom nil values (eg '-', 'n/a')
  • csvplusTrueVal & csvplusFalseVal (eg 'yes' and 'no' without custom types that implement Marshaler/Unmarshaler interfaces)
  • csvplusFormat to also handle floats via string formatting (eg %.3e)

PRs welcome.

Docs

https://godoc.org/github.com/j0hnsmith/csvplus

About

Marshal/Unmarshal CSV data to/from struct slices

Resources

License

Stars

Watchers

Forks

Packages

No packages published