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