Skip to content

Commit 2fb135b

Browse files
committed
handle end element event in the worksheet row/column iterator XML SAX parser
1 parent 66d85da commit 2fb135b

File tree

4 files changed

+144
-88
lines changed

4 files changed

+144
-88
lines changed

col.go

Lines changed: 64 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -97,20 +97,20 @@ func (cols *Cols) Rows() ([]string, error) {
9797
if token == nil {
9898
break
9999
}
100-
switch startElement := token.(type) {
100+
switch xmlElement := token.(type) {
101101
case xml.StartElement:
102-
inElement = startElement.Name.Local
102+
inElement = xmlElement.Name.Local
103103
if inElement == "row" {
104104
cellCol = 0
105105
cellRow++
106-
attrR, _ := attrValToInt("r", startElement.Attr)
106+
attrR, _ := attrValToInt("r", xmlElement.Attr)
107107
if attrR != 0 {
108108
cellRow = attrR
109109
}
110110
}
111111
if inElement == "c" {
112112
cellCol++
113-
for _, attr := range startElement.Attr {
113+
for _, attr := range xmlElement.Attr {
114114
if attr.Name.Local == "r" {
115115
if cellCol, cellRow, err = CellNameToCoordinates(attr.Value); err != nil {
116116
return rows, err
@@ -123,14 +123,59 @@ func (cols *Cols) Rows() ([]string, error) {
123123
}
124124
if cellCol == cols.curCol {
125125
colCell := xlsxC{}
126-
_ = decoder.DecodeElement(&colCell, &startElement)
126+
_ = decoder.DecodeElement(&colCell, &xmlElement)
127127
val, _ := colCell.getValueFrom(cols.f, d)
128128
rows = append(rows, val)
129129
}
130130
}
131+
case xml.EndElement:
132+
if xmlElement.Name.Local == "sheetData" {
133+
return rows, err
134+
}
135+
}
136+
}
137+
return rows, err
138+
}
139+
140+
// columnXMLIterator defined runtime use field for the worksheet column SAX parser.
141+
type columnXMLIterator struct {
142+
err error
143+
inElement string
144+
cols Cols
145+
cellCol, curRow, row int
146+
}
147+
148+
// columnXMLHandler parse the column XML element of the worksheet.
149+
func columnXMLHandler(colIterator *columnXMLIterator, xmlElement *xml.StartElement) {
150+
colIterator.err = nil
151+
inElement := xmlElement.Name.Local
152+
if inElement == "row" {
153+
colIterator.row++
154+
for _, attr := range xmlElement.Attr {
155+
if attr.Name.Local == "r" {
156+
if colIterator.curRow, colIterator.err = strconv.Atoi(attr.Value); colIterator.err != nil {
157+
return
158+
}
159+
colIterator.row = colIterator.curRow
160+
}
161+
}
162+
colIterator.cols.totalRow = colIterator.row
163+
colIterator.cellCol = 0
164+
}
165+
if inElement == "c" {
166+
colIterator.cellCol++
167+
for _, attr := range xmlElement.Attr {
168+
if attr.Name.Local == "r" {
169+
if colIterator.cellCol, _, colIterator.err = CellNameToCoordinates(attr.Value); colIterator.err != nil {
170+
return
171+
}
172+
}
173+
}
174+
if colIterator.cellCol > colIterator.cols.totalCol {
175+
colIterator.cols.totalCol = colIterator.cellCol
131176
}
132177
}
133-
return rows, nil
178+
return
134179
}
135180

136181
// Cols returns a columns iterator, used for streaming reading data for a
@@ -161,53 +206,29 @@ func (f *File) Cols(sheet string) (*Cols, error) {
161206
output, _ := xml.Marshal(f.Sheet[name])
162207
f.saveFileList(name, f.replaceNameSpaceBytes(name, output))
163208
}
164-
var (
165-
inElement string
166-
cols Cols
167-
cellCol, curRow, row int
168-
err error
169-
)
170-
cols.sheetXML = f.readXML(name)
171-
decoder := f.xmlNewDecoder(bytes.NewReader(cols.sheetXML))
209+
var colIterator columnXMLIterator
210+
colIterator.cols.sheetXML = f.readXML(name)
211+
decoder := f.xmlNewDecoder(bytes.NewReader(colIterator.cols.sheetXML))
172212
for {
173213
token, _ := decoder.Token()
174214
if token == nil {
175215
break
176216
}
177-
switch startElement := token.(type) {
217+
switch xmlElement := token.(type) {
178218
case xml.StartElement:
179-
inElement = startElement.Name.Local
180-
if inElement == "row" {
181-
row++
182-
for _, attr := range startElement.Attr {
183-
if attr.Name.Local == "r" {
184-
if curRow, err = strconv.Atoi(attr.Value); err != nil {
185-
return &cols, err
186-
}
187-
row = curRow
188-
}
189-
}
190-
cols.totalRow = row
191-
cellCol = 0
219+
columnXMLHandler(&colIterator, &xmlElement)
220+
if colIterator.err != nil {
221+
return &colIterator.cols, colIterator.err
192222
}
193-
if inElement == "c" {
194-
cellCol++
195-
for _, attr := range startElement.Attr {
196-
if attr.Name.Local == "r" {
197-
if cellCol, _, err = CellNameToCoordinates(attr.Value); err != nil {
198-
return &cols, err
199-
}
200-
}
201-
}
202-
if cellCol > cols.totalCol {
203-
cols.totalCol = cellCol
204-
}
223+
case xml.EndElement:
224+
if xmlElement.Name.Local == "sheetData" {
225+
colIterator.cols.f = f
226+
colIterator.cols.sheet = trimSheetName(sheet)
227+
return &colIterator.cols, nil
205228
}
206229
}
207230
}
208-
cols.f = f
209-
cols.sheet = trimSheetName(sheet)
210-
return &cols, nil
231+
return &colIterator.cols, nil
211232
}
212233

213234
// GetColVisible provides a function to get visible of a single column by given

col_test.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,10 +148,20 @@ func TestColsRows(t *testing.T) {
148148
},
149149
}
150150

151-
cols.stashCol, cols.curCol = 0, 1
151+
f = NewFile()
152+
f.XLSX["xl/worksheets/sheet1.xml"] = nil
153+
cols, err = f.Cols("Sheet1")
154+
if !assert.NoError(t, err) {
155+
t.FailNow()
156+
}
157+
f = NewFile()
152158
cols, err = f.Cols("Sheet1")
159+
if !assert.NoError(t, err) {
160+
t.FailNow()
161+
}
162+
_, err = cols.Rows()
153163
assert.NoError(t, err)
154-
164+
cols.stashCol, cols.curCol = 0, 1
155165
// Test if token is nil
156166
cols.sheetXML = nil
157167
_, err = cols.Rows()

rows.go

Lines changed: 64 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -78,60 +78,48 @@ func (rows *Rows) Error() error {
7878

7979
// Columns return the current row's column values.
8080
func (rows *Rows) Columns() ([]string, error) {
81-
var (
82-
err error
83-
inElement string
84-
attrR, cellCol, row int
85-
columns []string
86-
)
87-
81+
var rowIterator rowXMLIterator
8882
if rows.stashRow >= rows.curRow {
89-
return columns, err
83+
return rowIterator.columns, rowIterator.err
9084
}
91-
92-
d := rows.f.sharedStringsReader()
85+
rowIterator.rows = rows
86+
rowIterator.d = rows.f.sharedStringsReader()
9387
for {
9488
token, _ := rows.decoder.Token()
9589
if token == nil {
9690
break
9791
}
98-
switch startElement := token.(type) {
92+
switch xmlElement := token.(type) {
9993
case xml.StartElement:
100-
inElement = startElement.Name.Local
101-
if inElement == "row" {
102-
row++
103-
if attrR, err = attrValToInt("r", startElement.Attr); attrR != 0 {
104-
row = attrR
94+
rowIterator.inElement = xmlElement.Name.Local
95+
if rowIterator.inElement == "row" {
96+
rowIterator.row++
97+
if rowIterator.attrR, rowIterator.err = attrValToInt("r", xmlElement.Attr); rowIterator.attrR != 0 {
98+
rowIterator.row = rowIterator.attrR
10599
}
106-
if row > rows.curRow {
107-
rows.stashRow = row - 1
108-
return columns, err
100+
if rowIterator.row > rowIterator.rows.curRow {
101+
rowIterator.rows.stashRow = rowIterator.row - 1
102+
return rowIterator.columns, rowIterator.err
109103
}
110104
}
111-
if inElement == "c" {
112-
cellCol++
113-
colCell := xlsxC{}
114-
_ = rows.decoder.DecodeElement(&colCell, &startElement)
115-
if colCell.R != "" {
116-
if cellCol, _, err = CellNameToCoordinates(colCell.R); err != nil {
117-
return columns, err
118-
}
119-
}
120-
blank := cellCol - len(columns)
121-
val, _ := colCell.getValueFrom(rows.f, d)
122-
columns = append(appendSpace(blank, columns), val)
105+
rowXMLHandler(&rowIterator, &xmlElement)
106+
if rowIterator.err != nil {
107+
return rowIterator.columns, rowIterator.err
123108
}
124109
case xml.EndElement:
125-
inElement = startElement.Name.Local
126-
if row == 0 {
127-
row = rows.curRow
110+
rowIterator.inElement = xmlElement.Name.Local
111+
if rowIterator.row == 0 {
112+
rowIterator.row = rowIterator.rows.curRow
128113
}
129-
if inElement == "row" && row+1 < rows.curRow {
130-
return columns, err
114+
if rowIterator.inElement == "row" && rowIterator.row+1 < rowIterator.rows.curRow {
115+
return rowIterator.columns, rowIterator.err
116+
}
117+
if rowIterator.inElement == "sheetData" {
118+
return rowIterator.columns, rowIterator.err
131119
}
132120
}
133121
}
134-
return columns, err
122+
return rowIterator.columns, rowIterator.err
135123
}
136124

137125
// appendSpace append blank characters to slice by given length and source slice.
@@ -151,6 +139,35 @@ func (err ErrSheetNotExist) Error() string {
151139
return fmt.Sprintf("sheet %s is not exist", string(err.SheetName))
152140
}
153141

142+
// rowXMLIterator defined runtime use field for the worksheet row SAX parser.
143+
type rowXMLIterator struct {
144+
err error
145+
inElement string
146+
attrR, cellCol, row int
147+
columns []string
148+
rows *Rows
149+
d *xlsxSST
150+
}
151+
152+
// rowXMLHandler parse the row XML element of the worksheet.
153+
func rowXMLHandler(rowIterator *rowXMLIterator, xmlElement *xml.StartElement) {
154+
rowIterator.err = nil
155+
if rowIterator.inElement == "c" {
156+
rowIterator.cellCol++
157+
colCell := xlsxC{}
158+
_ = rowIterator.rows.decoder.DecodeElement(&colCell, xmlElement)
159+
if colCell.R != "" {
160+
if rowIterator.cellCol, _, rowIterator.err = CellNameToCoordinates(colCell.R); rowIterator.err != nil {
161+
return
162+
}
163+
}
164+
blank := rowIterator.cellCol - len(rowIterator.columns)
165+
val, _ := colCell.getValueFrom(rowIterator.rows.f, rowIterator.d)
166+
rowIterator.columns = append(appendSpace(blank, rowIterator.columns), val)
167+
}
168+
return
169+
}
170+
154171
// Rows returns a rows iterator, used for streaming reading data for a
155172
// worksheet with a large data. For example:
156173
//
@@ -192,12 +209,12 @@ func (f *File) Rows(sheet string) (*Rows, error) {
192209
if token == nil {
193210
break
194211
}
195-
switch startElement := token.(type) {
212+
switch xmlElement := token.(type) {
196213
case xml.StartElement:
197-
inElement = startElement.Name.Local
214+
inElement = xmlElement.Name.Local
198215
if inElement == "row" {
199216
row++
200-
for _, attr := range startElement.Attr {
217+
for _, attr := range xmlElement.Attr {
201218
if attr.Name.Local == "r" {
202219
row, err = strconv.Atoi(attr.Value)
203220
if err != nil {
@@ -207,12 +224,16 @@ func (f *File) Rows(sheet string) (*Rows, error) {
207224
}
208225
rows.totalRow = row
209226
}
227+
case xml.EndElement:
228+
if xmlElement.Name.Local == "sheetData" {
229+
rows.f = f
230+
rows.sheet = name
231+
rows.decoder = f.xmlNewDecoder(bytes.NewReader(f.readXML(name)))
232+
return &rows, nil
233+
}
210234
default:
211235
}
212236
}
213-
rows.f = f
214-
rows.sheet = name
215-
rows.decoder = f.xmlNewDecoder(bytes.NewReader(f.readXML(name)))
216237
return &rows, nil
217238
}
218239

rows_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ func TestRows(t *testing.T) {
4646
f.XLSX["xl/worksheets/sheet1.xml"] = []byte(`<worksheet><sheetData><row r="1"><c r="A1" t="s"><v>1</v></c></row><row r="A"><c r="2" t="str"><v>B</v></c></row></sheetData></worksheet>`)
4747
_, err = f.Rows("Sheet1")
4848
assert.EqualError(t, err, `strconv.Atoi: parsing "A": invalid syntax`)
49+
50+
f.XLSX["xl/worksheets/sheet1.xml"] = nil
51+
_, err = f.Rows("Sheet1")
52+
assert.NoError(t, err)
4953
}
5054

5155
func TestRowsIterator(t *testing.T) {

0 commit comments

Comments
 (0)