forked from tealeg/xlsx
-
Notifications
You must be signed in to change notification settings - Fork 0
/
read.go
132 lines (127 loc) · 3.01 KB
/
read.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
package xlsx
import (
"errors"
"reflect"
"strconv"
"time"
)
var (
errNilInterface = errors.New("nil pointer is not a valid argument")
errNotStructPointer = errors.New("argument must be a pointer to struct")
errInvalidTag = errors.New(`invalid tag: must have the format xlsx:idx`)
)
//XLSXUnmarshaler is the interface implemented for types that can unmarshal a Row
//as a representation of themselves.
type XLSXUnmarshaler interface {
Unmarshal(*Row) error
}
//ReadStruct reads a struct from r to ptr. Accepts a ptr
//to struct. This code expects a tag xlsx:"N", where N is the index
//of the cell to be used. Basic types like int,string,float64 and bool
//are supported
func (r *Row) ReadStruct(ptr interface{}) error {
if ptr == nil {
return errNilInterface
}
//check if the type implements XLSXUnmarshaler. If so,
//just let it do the work.
unmarshaller, ok := ptr.(XLSXUnmarshaler)
if ok {
return unmarshaller.Unmarshal(r)
}
v := reflect.ValueOf(ptr)
if v.Kind() != reflect.Ptr {
return errNotStructPointer
}
v = v.Elem()
if v.Kind() != reflect.Struct {
return errNotStructPointer
}
n := v.NumField()
for i := 0; i < n; i++ {
field := v.Type().Field(i)
idx := field.Tag.Get("xlsx")
//do a recursive check for the field if it is a struct or a pointer
//even if it doesn't have a tag
//ignore if it has a - or empty tag
isTime := false
switch {
case idx == "-":
continue
case field.Type.Kind() == reflect.Ptr || field.Type.Kind() == reflect.Struct:
var structPtr interface{}
if !v.Field(i).CanSet() {
continue
}
if field.Type.Kind() == reflect.Struct {
structPtr = v.Field(i).Addr().Interface()
} else {
structPtr = v.Field(i).Interface()
}
//check if the container is a time.Time
_, isTime = structPtr.(*time.Time)
if isTime {
break
}
err := r.ReadStruct(structPtr)
if err != nil {
return err
}
continue
case len(idx) == 0:
continue
}
pos, err := strconv.Atoi(idx)
if err != nil {
return errInvalidTag
}
//check if desired position is not out of bounds
if pos > len(r.Cells)-1 {
continue
}
cell := r.Cells[pos]
fieldV := v.Field(i)
//continue if the field is not settable
if !fieldV.CanSet() {
continue
}
if isTime {
t, err := cell.GetTime(false)
if err != nil {
return err
}
if field.Type.Kind() == reflect.Ptr {
fieldV.Set(reflect.ValueOf(&t))
} else {
fieldV.Set(reflect.ValueOf(t))
}
continue
}
switch field.Type.Kind() {
case reflect.String:
value, err := cell.FormattedValue()
if err != nil {
return err
}
fieldV.SetString(value)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
value, err := cell.Int64()
if err != nil {
return err
}
fieldV.SetInt(value)
case reflect.Float64:
value, err := cell.Float()
if err != nil {
return err
}
fieldV.SetFloat(value)
case reflect.Bool:
value := cell.Bool()
fieldV.SetBool(value)
}
}
value := v.Interface()
ptr = &value
return nil
}