/
xs.go
163 lines (139 loc) · 3.87 KB
/
xs.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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
package xs
import (
"bytes"
"github.com/tealeg/xlsx"
"reflect"
"strconv"
"strings"
)
// UnmarshalFromFile read the xlsx file and parses the data and stores the result
// in the value pointed to by v. If v is nil or not a pointer,
// Unmarshal returns an InvalidUnmarshalError.
func UnmarshalFromFile(filePath string, ss ...interface{}) error {
xlFile, err := xlsx.OpenFile(filePath)
if err != nil {
return &OpenFileError{filePath, err}
}
return unmarshal(xlFile, ss...)
}
// Unmarshal parses the xlsx data and stores the result
// in the value pointed to by v. If v is nil or not a pointer,
// Unmarshal returns an InvalidUnmarshalError.
func Unmarshal(body []byte, ss ...interface{}) error {
xlFile, err := xlsx.OpenBinary(body)
if err != nil {
return &OpenFileError{"body", err}
}
return unmarshal(xlFile, ss...)
}
// Marshal read the data from the slice of struct
// and write it into xlsx file. Every slice of struct
// will be written in a sheet
func Marshal(ss ...interface{}) ([]byte, error) {
var buf bytes.Buffer
xlFile, err := marshal(ss...)
if err != nil {
return nil, err
}
err = xlFile.Write(&buf)
return buf.Bytes(), err
}
func unmarshal(xlFile *xlsx.File, ss ...interface{}) error {
//检查sheet数和结构体的数目
//check if the num of sheet equals to the num of slice
if len(xlFile.Sheets) < len(ss) {
return &SheetAndSLiceMismatched{len(xlFile.Sheets), len(ss)}
}
//空文件不处理
//do not process empty file
if len(xlFile.Sheets) == 0 {
return nil
}
for sheetIndex, s := range ss {
//s应该是一个array或者slice的指针
//s should be array or slice of ptr
sValues := reflect.ValueOf(s)
if sValues.Kind() != reflect.Ptr || sValues.IsNil() {
return &InvalidUnmarshalError{Type: reflect.TypeOf(s)}
}
if sValues.Type().Elem().Kind() != reflect.Slice {
return &InvalidUnmarshalError{Type: sValues.Type()}
}
sheet := xlFile.Sheets[sheetIndex]
maxRow := getMaxNoneEmptyRow(*sheet)
tagInfo := GetTagInfo(s)
headerIndexMap, err := genHeaderIndexMap(tagInfo, *sheet)
if err != nil {
return err
}
//逐行读xlsx文件,并转化成结构体
//preprocess excel
mList := make([]map[string]*xlsx.Cell, maxRow)
for i := 0; i < maxRow; i++ {
row := sheet.Rows[i+1]
m := make(map[string]*xlsx.Cell, len(tagInfo.Headers))
for _, tag := range tagInfo.Headers {
//check if NonOmitempty tag could get the value
if !tagInfo.M[tag].Omitempty && row.Cells[headerIndexMap[tag]].String() == "" {
return &LackColError{i, tag}
} else {
m[tag] = row.Cells[headerIndexMap[tag]]
}
}
mList[i] = m
}
err = decode(mList, tagInfo, s)
if err != nil {
return err
}
}
return nil
}
func marshal(ss ...interface{}) (*xlsx.File, error) {
excel := xlsx.NewFile()
for index, s := range ss {
var sheet, _ = excel.AddSheet("sheet" + strconv.Itoa(index))
var sValues = reflect.ValueOf(s)
var tagInfo = GetTagInfo(s)
if sValues.Type().Kind() != reflect.Slice {
return nil, &InvalidMarshalError{Type: sValues.Type()}
}
encode(sheet, tagInfo, sValues)
}
return excel, nil
}
func genHeaderIndexMap(tagInfoMap TagInfoMap, sheet xlsx.Sheet) (map[string]int, error) {
var headerIndexMap = make(map[string]int)
//check if sheet contains 0 row
if len(sheet.Rows) == 0 {
return nil, &EmptySheetError{}
}
xlsxHeader := sheet.Rows[0]
for header, tagInfo := range tagInfoMap.M {
exist := true
if !tagInfo.Omitempty {
exist = false
}
for i, cell := range xlsxHeader.Cells {
if strings.TrimSpace(cell.String()) == header {
headerIndexMap[header] = i
exist = true
}
}
if !exist {
return headerIndexMap, &LackHeaderError{header}
}
}
return headerIndexMap, nil
}
func getMaxNoneEmptyRow(sheet xlsx.Sheet) int {
var marRow = 0
for i, row := range sheet.Rows {
for _, cell := range row.Cells {
if cell.String() != "" {
marRow = i
}
}
}
return marRow
}