modulepackage
0.0.0-20230308181706-a009a30a8ed2
Repository: https://github.com/j0hnsmith/csvplus.git
Documentation: pkg.go.dev
# README
csvplus
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 implementMarshaler
/Unmarshaler
interfaces)csvplusFormat
to also handle floats via string formatting (eg%.3e
)
PRs welcome.
Docs
# Functions
Marshal marshals v into csv data.
MarshalWithoutHeader writes csv data without a header row.
MarshalWriter marshals v into the given writer.
NewDecoder reads and decodes CSV records from r.
NewEncoder returns an initialised Encoder.
Unmarshal parses the csv encoded data and stores the result in the slice pointed to by v.
UnmarshalReader is the same as Unmarshal but takes it's input data from an io.Reader.
UnmarshalWithoutHeader is used to unmarshal csv data that doesn't have a header row.
# Structs
A Decoder reads and decodes CSV records from an input stream.
An Encoder writes csv data from a list of struct.
No description provided by the author
# Interfaces
Marshaler is the interface implemented by types that can marshal a csv value (string) of themselves.
Unmarshaler is the interface implemented by types that can unmarshal a csv record of themselves.