@@ -22,7 +22,6 @@ internal partial class ExcelOpenXmlSheetReader : IExcelReader
22
22
private static readonly string [ ] _relationshiopNs = { Config . SpreadsheetmlXmlRelationshipns , Config . SpreadsheetmlXmlStrictRelationshipns } ;
23
23
private List < SheetRecord > _sheetRecords ;
24
24
internal IDictionary < int , string > _sharedStrings ;
25
- private MergeCells _mergeCells ;
26
25
private ExcelOpenXmlStyles _style ;
27
26
internal readonly ExcelOpenXmlZip _archive ;
28
27
private readonly OpenXmlConfiguration _config ;
@@ -140,7 +139,6 @@ public IAsyncEnumerable<IDictionary<string, object>> QueryRangeAsync(bool useHea
140
139
return QueryImplAsync < T > ( QueryRangeAsync ( false , sheetName , startRowIndex , startColumnIndex , endRowIndex , endColumnIndex , cancellationToken ) , ReferenceHelper . ConvertXyToCell ( startColumnIndex , startRowIndex ) , hasHeader , _config , cancellationToken ) ;
141
140
}
142
141
143
-
144
142
[ Zomp . SyncMethodGenerator . CreateSyncVersion ]
145
143
internal async IAsyncEnumerable < IDictionary < string , object > > InternalQueryRangeAsync ( bool useHeaderRow , string sheetName , int startRowIndex , int startColumnIndex , int ? endRowIndex , int ? endColumnIndex , [ EnumeratorCancellation ] CancellationToken cancellationToken = default )
146
144
{
@@ -159,15 +157,12 @@ internal async IAsyncEnumerable<IDictionary<string, object>> InternalQueryRangeA
159
157
// TODO: need to optimize performance
160
158
// Q. why need 3 times openstream merge one open read? A. no, zipstream can't use position = 0
161
159
162
- var mergeCellsContext = new MergeCellsContext { } ;
163
-
160
+ var mergeCellsContext = new MergeCellsContext ( ) ;
164
161
if ( _config . FillMergedCells && ! await TryGetMergeCellsAsync ( sheetEntry , mergeCellsContext , cancellationToken ) . ConfigureAwait ( false ) )
165
162
{
166
163
yield break ;
167
164
}
168
165
169
- _mergeCells = mergeCellsContext . MergeCells ;
170
-
171
166
var maxRowColumnIndexResult = await TryGetMaxRowColumnIndexAsync ( sheetEntry , cancellationToken ) . ConfigureAwait ( false ) ;
172
167
if ( ! maxRowColumnIndexResult . IsSuccess )
173
168
{
@@ -223,89 +218,16 @@ internal async IAsyncEnumerable<IDictionary<string, object>> InternalQueryRangeA
223
218
break ;
224
219
}
225
220
226
- // fill empty rows
227
- if ( ! _config . IgnoreEmptyRows )
228
- {
229
- var expectedRowIndex = isFirstRow ? startRowIndex : nextRowIndex ;
230
- if ( startRowIndex <= expectedRowIndex && expectedRowIndex < rowIndex )
231
- {
232
- for ( int i = expectedRowIndex ; i < rowIndex ; i ++ )
233
- {
234
- yield return GetCell ( useHeaderRow , maxColumnIndex , headRows , startColumnIndex ) ;
235
- }
236
- }
237
- }
238
-
239
- // row -> c, must after `if (nextRowIndex < rowIndex)` condition code, eg. The first empty row has no xml element,and the second row xml element is <row r="2"/>
240
- if ( ! await XmlReaderHelper . ReadFirstContentAsync ( reader , cancellationToken ) . ConfigureAwait ( false ) && ! _config . IgnoreEmptyRows )
221
+ await foreach ( var row in QueryRowAsync ( reader , isFirstRow , startRowIndex , nextRowIndex , rowIndex , startColumnIndex , endColumnIndex , maxColumnIndex , withoutCR , useHeaderRow , headRows , mergeCellsContext . MergeCells , cancellationToken ) . ConfigureAwait ( false ) )
241
222
{
242
- //Fill in case of self closed empty row tag eg. <row r="1"/>
243
- yield return GetCell ( useHeaderRow , maxColumnIndex , headRows , startColumnIndex ) ;
244
- continue ;
245
- }
246
-
247
- #region Set Cells
248
-
249
- var cell = GetCell ( useHeaderRow , maxColumnIndex , headRows , startColumnIndex ) ;
250
- var columnIndex = withoutCR ? - 1 : 0 ;
251
- while ( ! reader . EOF )
252
- {
253
- if ( XmlReaderHelper . IsStartElement ( reader , "c" , _ns ) )
223
+ if ( isFirstRow )
254
224
{
255
- var aS = reader . GetAttribute ( "s" ) ;
256
- var aR = reader . GetAttribute ( "r" ) ;
257
- var aT = reader . GetAttribute ( "t" ) ;
258
- var cellAndColumn = await ReadCellAndSetColumnIndexAsync ( reader , columnIndex , withoutCR ,
259
- startColumnIndex , aR , aT , cancellationToken ) . ConfigureAwait ( false ) ;
260
-
261
- var cellValue = cellAndColumn . CellValue ;
262
- columnIndex = cellAndColumn . ColumnIndex ;
263
-
264
- if ( _config . FillMergedCells )
265
- {
266
- if ( _mergeCells . MergesValues . ContainsKey ( aR ) )
267
- {
268
- _mergeCells . MergesValues [ aR ] = cellValue ;
269
- }
270
- else if ( _mergeCells . MergesMap . TryGetValue ( aR , out var mergeKey ) )
271
- {
272
- _mergeCells . MergesValues . TryGetValue ( mergeKey , out cellValue ) ;
273
- }
274
- }
275
-
276
- if ( columnIndex < startColumnIndex || ( endColumnIndex . HasValue && columnIndex > endColumnIndex . Value ) )
225
+ isFirstRow = false ; // for startcell logic
226
+ if ( useHeaderRow )
277
227
continue ;
278
-
279
- if ( ! string . IsNullOrEmpty ( aS ) ) // if c with s meaning is custom style need to check type by xl/style.xml
280
- {
281
- int xfIndex = - 1 ;
282
- if ( int . TryParse ( aS , NumberStyles . Any , CultureInfo . InvariantCulture ,
283
- out var styleIndex ) )
284
- xfIndex = styleIndex ;
285
-
286
- // only when have s attribute then load styles xml data
287
- if ( _style == null )
288
- _style = new ExcelOpenXmlStyles ( _archive ) ;
289
-
290
- cellValue = _style . ConvertValueByStyleFormat ( xfIndex , cellValue ) ;
291
- }
292
-
293
- SetCellsValueAndHeaders ( cellValue , useHeaderRow , ref headRows , ref isFirstRow , ref cell , columnIndex ) ;
294
228
}
295
- else if ( ! await XmlReaderHelper . SkipContentAsync ( reader , cancellationToken ) . ConfigureAwait ( false ) )
296
- break ;
229
+ yield return row ;
297
230
}
298
-
299
- #endregion
300
-
301
- if ( isFirstRow )
302
- {
303
- isFirstRow = false ; // for startcell logic
304
- if ( useHeaderRow )
305
- continue ;
306
- }
307
-
308
- yield return cell ;
309
231
}
310
232
else if ( ! await XmlReaderHelper . SkipContentAsync ( reader , cancellationToken ) . ConfigureAwait ( false ) )
311
233
{
@@ -321,6 +243,94 @@ internal async IAsyncEnumerable<IDictionary<string, object>> InternalQueryRangeA
321
243
}
322
244
}
323
245
246
+ [ Zomp . SyncMethodGenerator . CreateSyncVersion ]
247
+ private async IAsyncEnumerable < IDictionary < string , object > > QueryRowAsync (
248
+ XmlReader reader ,
249
+ bool isFirstRow ,
250
+ int startRowIndex ,
251
+ int nextRowIndex ,
252
+ int rowIndex ,
253
+ int startColumnIndex ,
254
+ int ? endColumnIndex ,
255
+ int maxColumnIndex ,
256
+ bool withoutCR ,
257
+ bool useHeaderRow ,
258
+ Dictionary < int , string > headRows ,
259
+ MergeCells mergeCells ,
260
+ [ EnumeratorCancellation ] CancellationToken cancellationToken = default )
261
+ {
262
+ // fill empty rows
263
+ if ( ! _config . IgnoreEmptyRows )
264
+ {
265
+ var expectedRowIndex = isFirstRow ? startRowIndex : nextRowIndex ;
266
+ if ( startRowIndex <= expectedRowIndex && expectedRowIndex < rowIndex )
267
+ {
268
+ for ( int i = expectedRowIndex ; i < rowIndex ; i ++ )
269
+ {
270
+ yield return GetCell ( useHeaderRow , maxColumnIndex , headRows , startColumnIndex ) ;
271
+ }
272
+ }
273
+ }
274
+
275
+ // row -> c, must after `if (nextRowIndex < rowIndex)` condition code, eg. The first empty row has no xml element,and the second row xml element is <row r="2"/>
276
+ if ( ! await XmlReaderHelper . ReadFirstContentAsync ( reader , cancellationToken ) . ConfigureAwait ( false ) && ! _config . IgnoreEmptyRows )
277
+ {
278
+ //Fill in case of self closed empty row tag eg. <row r="1"/>
279
+ yield return GetCell ( useHeaderRow , maxColumnIndex , headRows , startColumnIndex ) ;
280
+ yield break ;
281
+ }
282
+
283
+ var cell = GetCell ( useHeaderRow , maxColumnIndex , headRows , startColumnIndex ) ;
284
+ var columnIndex = withoutCR ? - 1 : 0 ;
285
+ while ( ! reader . EOF )
286
+ {
287
+ if ( XmlReaderHelper . IsStartElement ( reader , "c" , _ns ) )
288
+ {
289
+ var aS = reader . GetAttribute ( "s" ) ;
290
+ var aR = reader . GetAttribute ( "r" ) ;
291
+ var aT = reader . GetAttribute ( "t" ) ;
292
+ var cellAndColumn = await ReadCellAndSetColumnIndexAsync ( reader , columnIndex , withoutCR , startColumnIndex , aR , aT , cancellationToken ) . ConfigureAwait ( false ) ;
293
+
294
+ var cellValue = cellAndColumn . CellValue ;
295
+ columnIndex = cellAndColumn . ColumnIndex ;
296
+
297
+ if ( _config . FillMergedCells )
298
+ {
299
+ if ( mergeCells . MergesValues . ContainsKey ( aR ) )
300
+ {
301
+ mergeCells . MergesValues [ aR ] = cellValue ;
302
+ }
303
+ else if ( mergeCells . MergesMap . TryGetValue ( aR , out var mergeKey ) )
304
+ {
305
+ mergeCells . MergesValues . TryGetValue ( mergeKey , out cellValue ) ;
306
+ }
307
+ }
308
+
309
+ if ( columnIndex < startColumnIndex || ( endColumnIndex . HasValue && columnIndex > endColumnIndex . Value ) )
310
+ continue ;
311
+
312
+ if ( ! string . IsNullOrEmpty ( aS ) ) // if c with s meaning is custom style need to check type by xl/style.xml
313
+ {
314
+ int xfIndex = - 1 ;
315
+ if ( int . TryParse ( aS , NumberStyles . Any , CultureInfo . InvariantCulture ,
316
+ out var styleIndex ) )
317
+ xfIndex = styleIndex ;
318
+
319
+ // only when have s attribute then load styles xml data
320
+ if ( _style == null )
321
+ _style = new ExcelOpenXmlStyles ( _archive ) ;
322
+
323
+ cellValue = _style . ConvertValueByStyleFormat ( xfIndex , cellValue ) ;
324
+ }
325
+
326
+ SetCellsValueAndHeaders ( cellValue , useHeaderRow , headRows , isFirstRow , cell , columnIndex ) ;
327
+ }
328
+ else if ( ! await XmlReaderHelper . SkipContentAsync ( reader , cancellationToken ) . ConfigureAwait ( false ) )
329
+ break ;
330
+ }
331
+ yield return cell ;
332
+ }
333
+
324
334
[ Zomp . SyncMethodGenerator . CreateSyncVersion ]
325
335
public static async IAsyncEnumerable < T > QueryImplAsync < T > ( IAsyncEnumerable < IDictionary < string , object > > values , string startCell , bool hasHeader , Configuration configuration , [ EnumeratorCancellation ] CancellationToken cancellationToken = default ) where T : class , new ( )
326
336
{
@@ -434,7 +444,7 @@ private static IDictionary<string, object> GetCell(bool useHeaderRow, int maxCol
434
444
return useHeaderRow ? CustomPropertyHelper . GetEmptyExpandoObject ( headRows ) : CustomPropertyHelper . GetEmptyExpandoObject ( maxColumnIndex , startColumnIndex ) ;
435
445
}
436
446
437
- private static void SetCellsValueAndHeaders ( object cellValue , bool useHeaderRow , ref Dictionary < int , string > headRows , ref bool isFirstRow , ref IDictionary < string , object > cell , int columnIndex )
447
+ private static void SetCellsValueAndHeaders ( object cellValue , bool useHeaderRow , Dictionary < int , string > headRows , bool isFirstRow , IDictionary < string , object > cell , int columnIndex )
438
448
{
439
449
if ( ! useHeaderRow )
440
450
{
@@ -1093,7 +1103,7 @@ internal static async Task<bool> TryGetMergeCellsAsync(ZipArchiveEntry sheetEntr
1093
1103
#else
1094
1104
true
1095
1105
#endif
1096
- ) ;
1106
+ ) ;
1097
1107
var mergeCells = new MergeCells ( ) ;
1098
1108
using ( var sheetStream = sheetEntry . Open ( ) )
1099
1109
using ( XmlReader reader = XmlReader . Create ( sheetStream , xmlSettings ) )
0 commit comments