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
}
|