-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathexample_csv_test.go
138 lines (129 loc) · 3.85 KB
/
example_csv_test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
package set_test
import (
"encoding/csv"
"errors"
"fmt"
"io"
"reflect"
"strings"
"github.com/nofeaturesonlybugs/set"
)
// This example demonstrates how Mapper might be used to create a general
// purpose CSV unmarshaler.
// CSVUnmarshaler is a general purpose CSV unmarshaler.
type CSVUnmarshaler struct {
Mapper *set.Mapper
}
// Load reads CSV data from r and deserializes each row into dst.
func (u CSVUnmarshaler) Load(r io.Reader, dst interface{}) error {
// Expect dst to be a *[]T or pointer chain to []T where T is struct or pointer chain to struct.
slice, err := set.Slice(dst)
if err != nil {
return err
} else if slice.ElemEndType.Kind() != reflect.Struct {
return fmt.Errorf("dst elements should be struct or pointer to struct")
}
//
m := u.Mapper
if m == nil {
// When the Mapper member is nil use a default mapper.
m = &set.Mapper{
Tags: []string{"csv"},
}
}
//
// Now the CSV can be read, processed, and deserialized into dst.
c := csv.NewReader(r)
//
// Load the column names; these will be used later when calling BoundMapping.Set
headers, err := c.Read()
if err != nil {
return err
}
c.ReuseRecord = true // From this point forward the csv reader can reuse the slice.
//
// Create a BoundMapping for element's type.
b, err := m.Bind(slice.Elem())
if err != nil {
return err
}
for {
row, err := c.Read()
if errors.Is(err, io.EOF) {
return nil
} else if err != nil {
return err
}
// Create a new element and bind to it.
elemValue := slice.Elem() // Elem() returns reflect.Value and Rebind() conveniently
b.Rebind(elemValue) // allows reflect.Value as an argument.
// Row is a slice of data and headers has the mapped names in corresponding indexes.
for k, columnName := range headers {
_ = b.Set(columnName, row[k]) // err will be checked after iteration.
}
// b.Err() returns the first error encountered from b.Set()
if err = b.Err(); err != nil {
return err
}
// Append to dst.
slice.Append(elemValue)
}
}
func Example_cSVUnmarshaler() {
type Address struct {
ID int `json:"id"`
Street string `json:"street" csv:"address"`
City string `json:"city"`
State string `json:"state"`
Zip string `json:"zip" csv:"postal"`
}
PrintAddresses := func(all []Address) {
for _, a := range all {
fmt.Printf("ID=%v %v, %v, %v %v\n", a.ID, a.Street, a.City, a.State, a.Zip)
}
}
//
loader := CSVUnmarshaler{
Mapper: &set.Mapper{
// Tags are listed in order of priority so csv has higher priority than json
// when generating mapped names.
Tags: []string{"csv", "json"},
},
}
var data = `id,address,city,state,postal
1,06 Hoepker Court,Jacksonville,Florida,32209
2,92 Cody Hill,Falls Church,Virginia,22047
3,242 Burning Wood Terrace,Fort Worth,Texas,76105
4,41 Clarendon Pass,Fort Myers,Florida,33913
`
var addresses []Address
err := loader.Load(strings.NewReader(data), &addresses)
if err != nil {
fmt.Println(err)
return
}
PrintAddresses(addresses)
// Notice here this data has the columns in a different order
fmt.Println()
data = `city,address,postal,id,state
Tuscaloosa,2607 Hanson Junction,35487,1,Alabama
Bakersfield,2 Sherman Place,93305,2,California
Kansas City,4 Porter Place,64199,3,Missouri
New York City,23 Sachtjen Alley,10160,4,New York`
addresses = addresses[0:0]
err = loader.Load(strings.NewReader(data), &addresses)
if err != nil {
fmt.Println(err)
return
}
PrintAddresses(addresses)
// Output: ID=1 06 Hoepker Court, Jacksonville, Florida 32209
// ID=2 92 Cody Hill, Falls Church, Virginia 22047
// ID=3 242 Burning Wood Terrace, Fort Worth, Texas 76105
// ID=4 41 Clarendon Pass, Fort Myers, Florida 33913
//
// ID=1 2607 Hanson Junction, Tuscaloosa, Alabama 35487
// ID=2 2 Sherman Place, Bakersfield, California 93305
// ID=3 4 Porter Place, Kansas City, Missouri 64199
// ID=4 23 Sachtjen Alley, New York City, New York 10160
}