Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/MiniExcel.Core/Reflection/MiniExcelMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
{
if (headersDic?.TryGetValue(alias, out var columnId) is true)
{
var columnName = keys[columnId];

Check warning on line 46 in src/MiniExcel.Core/Reflection/MiniExcelMapper.cs

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.
item.TryGetValue(columnName, out var aliasItemValue);

if (aliasItemValue is not null)
Expand All @@ -56,13 +56,13 @@

//Q: Why need to check every time? A: it needs to check everytime, because it's dictionary
object? itemValue = null;
if (map.ExcelIndexName is not null && (keys?.Contains(map.ExcelIndexName) is true))
if (map.ExcelIndexName is not null && keys?.Contains(map.ExcelIndexName) is true)
{
item.TryGetValue(map.ExcelIndexName, out itemValue);
}
else if (map.ExcelColumnName is not null && (headersDic?.TryGetValue(map.ExcelColumnName, out var columnId) is true))
else if (map.ExcelColumnName is not null && headersDic?.TryGetValue(map.ExcelColumnName, out var columnId) is true)
{
var columnName = keys[columnId];

Check warning on line 65 in src/MiniExcel.Core/Reflection/MiniExcelMapper.cs

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.
item.TryGetValue(columnName, out itemValue);
}

Expand Down
95 changes: 92 additions & 3 deletions src/MiniExcel.OpenXml/Api/OpenXmlImporter.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using MiniExcelLib.OpenXml.Reader;

// ReSharper disable once CheckNamespace
namespace MiniExcelLib.OpenXml;

Expand Down Expand Up @@ -326,7 +328,7 @@ public async Task<List<string>> GetSheetNamesAsync(Stream stream, bool leaveOpen
await using var disposableArchive = archive.ConfigureAwait(false);
using var reader = await OpenXmlReader.CreateAsync(stream, null, leaveOpen, cancellationToken).ConfigureAwait(false);

var rels = await reader.GetWorkbookRelsAsync(archive.EntryCollection, cancellationToken).ConfigureAwait(false);
var rels = await OpenXmlReader.GetWorkbookRelsAsync(archive.EntryCollection, cancellationToken).ConfigureAwait(false);
return rels?.Select(s => s.Name).ToList() ?? [];
}

Expand Down Expand Up @@ -366,7 +368,7 @@ public async Task<List<SheetInfo>> GetSheetInformationsAsync(Stream stream, bool
await using var disposableArchve = archive.ConfigureAwait(false);
using var reader = await OpenXmlReader.CreateAsync(stream, null, leaveOpen, cancellationToken).ConfigureAwait(false);

var rels = await reader.GetWorkbookRelsAsync(archive.EntryCollection, cancellationToken).ConfigureAwait(false);
var rels = await OpenXmlReader.GetWorkbookRelsAsync(archive.EntryCollection, cancellationToken).ConfigureAwait(false);
return rels?.Select((s, i) => s.ToSheetInfo((uint)i)).ToList() ?? [];
}

Expand Down Expand Up @@ -433,7 +435,6 @@ public async Task<ICollection<string>> GetColumnNamesAsync(string path, bool has
return await GetColumnNamesAsync(stream, hasHeaderRow, sheetName, startCell, false, cancellationToken).ConfigureAwait(false);
}


/// <summary>
/// Retrieves the column names from the first row (header row) of an Excel sheet.
/// </summary>
Expand Down Expand Up @@ -497,6 +498,94 @@ public async Task<CommentResultSet> RetrieveCommentsAsync(Stream stream, string?
return await reader.ReadCommentsAsync(sheetName, cancellationToken).ConfigureAwait(false);
}

/// <summary>
/// Queries a named table in an Excel worksheet and returns dynamic objects representing each row.
/// </summary>
/// <param name="path">The path to the Excel document.</param>
/// <param name="sheetName">The name of the worksheet containing the table. Default is "Sheet1".</param>
/// <param name="tableName">The name of the table to query. Default is "Table1".</param>
/// <param name="cancellationToken">A token to cancel the asynchronous operation.</param>
/// <remarks>
/// Named tables in Excel are structured data ranges with defined column headers and a unique name.
/// This method reads from the specified table within a stream and yields rows as dynamic objects with properties based on the table's column names.
/// </remarks>
[CreateSyncVersion]
public async IAsyncEnumerable<dynamic> QueryTableAsync(string path, string sheetName = "Sheet1", string tableName = "Table1", [EnumeratorCancellation] CancellationToken cancellationToken = default)
{
var stream = FileHelper.OpenSharedRead(path);
await using var disposableStream = stream.ConfigureAwait(false);

using var reader = await OpenXmlReader.CreateAsync(stream, null, false, cancellationToken).ConfigureAwait(false);
await foreach (var table in reader.QueryTableAsync(sheetName, tableName, false, cancellationToken).ConfigureAwait(false))
yield return table;
}

/// <summary>
/// Queries a named table in an Excel worksheet and returns dynamic objects representing each row.
/// </summary>
/// <param name="stream">The stream containing the Excel file data. The stream position is not reset after reading.</param>
/// <param name="sheetName">The name of the worksheet containing the table. Default is "Sheet1".</param>
/// <param name="tableName">The name of the table to query. Default is "Table1".</param>
/// <param name="leaveOpen">True to leave the stream open after the query is completed, otherwise false.</param>
/// <param name="cancellationToken">A token to cancel the asynchronous operation.</param>
/// <remarks>
/// Named tables in Excel are structured data ranges with defined column headers and a unique name.
/// This method reads from the specified table within a stream and yields rows as dynamic objects with properties based on the table's column names.
/// </remarks>
[CreateSyncVersion]
public async IAsyncEnumerable<dynamic> QueryTableAsync(Stream stream, string sheetName = "Sheet1", string tableName = "Table1", bool leaveOpen = false, [EnumeratorCancellation] CancellationToken cancellationToken = default)
{
using var reader = await OpenXmlReader.CreateAsync(stream, null, leaveOpen, cancellationToken).ConfigureAwait(false);
await foreach (var table in reader.QueryTableAsync(sheetName, tableName, false, cancellationToken).ConfigureAwait(false))
yield return table;
}

/// <summary>
/// Queries a named table in an Excel worksheet and returns strongly-typed objects representing each row.
/// </summary>
/// <typeparam name="T">The class type to map each row to. Must have a parameterless constructor. Property names should match the table's column names.</typeparam>
/// <param name="path">The path to the Excel document. The stream position is not reset after reading.</param>
/// <param name="sheetName">The name of the worksheet containing the table. Default is "Sheet1".</param>
/// <param name="tableName">The name of the table to query. Default is "Table1".</param>
/// <param name="cancellationToken">A token to cancel the asynchronous operation.</param>
/// <remarks>
/// Named tables in Excel are structured data ranges with defined column headers and a unique name.
/// This method reads from the specified table within a stream and maps each row to an instance of the provided type. The mapping is based on property/field names matching column headers.
/// </remarks>
[CreateSyncVersion]
public async IAsyncEnumerable<T> QueryTableAsync<T>(string path, string sheetName = "Sheet1", string tableName = "Table1", [EnumeratorCancellation] CancellationToken cancellationToken = default)
where T : class, new()
{
var stream = FileHelper.OpenSharedRead(path);
await using var disposableStream = stream.ConfigureAwait(false);

using var reader = await OpenXmlReader.CreateAsync(stream, null, false, cancellationToken).ConfigureAwait(false);
await foreach (var table in reader.QueryTableAsync<T>(sheetName, tableName, cancellationToken).ConfigureAwait(false))
yield return table;
}

/// <summary>
/// Queries a named table in an Excel worksheet and returns strongly-typed objects representing each row.
/// </summary>
/// <typeparam name="T">The class type to map each row to. Must have a parameterless constructor. Property names should match the table's column names.</typeparam>
/// <param name="stream">The stream containing the Excel file data. The stream position is not reset after reading.</param>
/// <param name="sheetName">The name of the worksheet containing the table. Default is "Sheet1".</param>
/// <param name="tableName">The name of the table to query. Default is "Table1".</param>
/// <param name="leaveOpen">True to leave the stream open after the query is completed, otherwise false.</param>
/// <param name="cancellationToken">A token to cancel the asynchronous operation.</param>
/// <remarks>
/// Named tables in Excel are structured data ranges with defined column headers and a unique name.
/// This method reads from the specified table within a stream and maps each row to an instance of the provided type. The mapping is based on property/field names matching column headers.
/// </remarks>
[CreateSyncVersion]
public async IAsyncEnumerable<T> QueryTableAsync<T>(Stream stream, string sheetName = "Sheet1", string tableName = "Table1", bool leaveOpen = false, [EnumeratorCancellation] CancellationToken cancellationToken = default)
where T : class, new()
{
using var reader = await OpenXmlReader.CreateAsync(stream, null, leaveOpen, cancellationToken).ConfigureAwait(false);
await foreach (var table in reader.QueryTableAsync<T>(sheetName, tableName, cancellationToken).ConfigureAwait(false))
yield return table;
}

#endregion

#region DataReader
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
using System.Text.RegularExpressions;

namespace MiniExcelLib.OpenXml.FluentMapping.Configuration;

internal partial class CollectionMappingBuilder<T, TCollection> : ICollectionMappingBuilder<T, TCollection> where TCollection : IEnumerable
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
using System.Text.RegularExpressions;

namespace MiniExcelLib.OpenXml.FluentMapping.Configuration;

internal partial class PropertyMappingBuilder<T, TProperty> : IPropertyMappingBuilder<T, TProperty>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using System.Collections.Concurrent;
using System.Globalization;
using System.Reflection;

namespace MiniExcelLib.OpenXml.FluentMapping.Helpers;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
using System.Reflection;
using MiniExcelLib.Core.Helpers;
using MiniExcelLib.Core.Reflection;
using MiniExcelLib.OpenXml.FluentMapping.Helpers;

namespace MiniExcelLib.OpenXml.FluentMapping;
Expand Down
2 changes: 0 additions & 2 deletions src/MiniExcel.OpenXml/FluentMapping/MappingCellStream.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
using MiniExcelLib.Core.Abstractions;

namespace MiniExcelLib.OpenXml.FluentMapping;

internal readonly struct MappingCellStream<T>(IEnumerable<T> items, CompiledMapping<T> mapping, string[] columnLetters) : IMappingCellStream
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
using MiniExcelLib.Core.Abstractions;
using MiniExcelLib.Core.Reflection;

namespace MiniExcelLib.OpenXml.FluentMapping;

internal class MappingCellStreamAdapter<T>(MappingCellStream<T> cellStream, string[] columnLetters)
Expand Down
4 changes: 0 additions & 4 deletions src/MiniExcel.OpenXml/FluentMapping/MappingCompiler.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
using System.Reflection;
using MiniExcelLib.Core.Helpers;
using MiniExcelLib.Core.Reflection;
using MiniExcelLib.OpenXml.FluentMapping.Configuration;
using MiniExcelLib.OpenXml.FluentMapping.Helpers;
using MiniExcelLib.OpenXml.Utils;

namespace MiniExcelLib.OpenXml.FluentMapping;

Expand Down
7 changes: 4 additions & 3 deletions src/MiniExcel.OpenXml/FluentMapping/MappingReader.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using MiniExcelLib.OpenXml.Reader;

namespace MiniExcelLib.OpenXml.FluentMapping;

internal static partial class MappingReader<T> where T : class, new()
Expand Down Expand Up @@ -203,8 +205,7 @@ private static Dictionary<int, IList> InitializeCollections(CompiledMapping<T> m
else
{
// This should never happen with properly optimized mappings
throw new InvalidOperationException(
"OptimizedCollectionHelpers is null. Ensure the mapping was properly compiled and optimized.");
throw new InvalidOperationException("OptimizedCollectionHelpers is null. Ensure the mapping was properly compiled and optimized.");
}

return collections;
Expand Down Expand Up @@ -469,4 +470,4 @@ private static bool HasAnyData(T item, CompiledMapping<T> mapping)
bool b => !b,
_ => false
};
}
}
21 changes: 2 additions & 19 deletions src/MiniExcel.OpenXml/FluentMapping/MappingTemplateProcessor.cs
Original file line number Diff line number Diff line change
@@ -1,27 +1,11 @@
using System.Text;
using System.Xml;
using MiniExcelLib.OpenXml.Helpers;
using MiniExcelLib.OpenXml.Utils;
using Zomp.SyncMethodGenerator;

namespace MiniExcelLib.OpenXml.FluentMapping;

internal partial struct MappingTemplateProcessor<T>(CompiledMapping<T> mapping) where T : class
{
[CreateSyncVersion]
public async Task ProcessSheetAsync(
Stream sourceStream,
Stream targetStream,
IEnumerator<T> dataEnumerator,
CancellationToken cancellationToken)
public async Task ProcessSheetAsync(Stream sourceStream, Stream targetStream, IEnumerator<T> dataEnumerator, CancellationToken cancellationToken)
{
var readerSettings = new XmlReaderSettings
{
Async = true,
IgnoreWhitespace = false,
IgnoreComments = false,
CheckCharacters = false
};
var readerSettings = XmlReaderHelper.GetXmlReaderSettings();

var writerSettings = new XmlWriterSettings
{
Expand All @@ -38,7 +22,6 @@ public async Task ProcessSheetAsync(
var currentItem = dataEnumerator.MoveNext() ? dataEnumerator.Current : null;
var currentItemIndex = currentItem is not null ? 0 : -1;


// Track which rows have been written from the template
var writtenRows = new HashSet<int>();

Expand Down
4 changes: 2 additions & 2 deletions src/MiniExcel.OpenXml/Models/ExcelRange.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ internal ExcelRangeElement(int startIndex, int endIndex)

public class ExcelRange(int maxRow, int maxColumn)
{
public string StartCell { get; internal set; }
public string EndCell { get; internal set; }
public string? StartCell { get; internal set; }
public string? EndCell { get; internal set; }

public ExcelRangeElement Rows { get; } = new(1, maxRow);
public ExcelRangeElement Columns { get; } = new(1, maxColumn);
Expand Down
17 changes: 17 additions & 0 deletions src/MiniExcel.OpenXml/Models/TableInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace MiniExcelLib.OpenXml.Models;

public class TableInfo
{
internal TableInfo(string name, IEnumerable<string> columns, string? referenceCells, bool hiddenHeader)
{
Name = name;
Columns = [..columns];
ReferenceCells = referenceCells;
HiddenHeader = hiddenHeader;
}

public string Name { get; private set; }
public string[] Columns { get; private set; }
public string? ReferenceCells { get; private set; }
public bool HiddenHeader { get; private set; }
}
3 changes: 2 additions & 1 deletion src/MiniExcel.OpenXml/Picture/OpenXmlPictureImplement.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Drawing;
using MiniExcelLib.OpenXml.Reader;

namespace MiniExcelLib.OpenXml.Picture;

Expand Down Expand Up @@ -27,7 +28,7 @@ public static async Task AddPictureAsync(Stream excelStream, CancellationToken c
#else
using var archive = new ZipArchive(excelStream, ZipArchiveMode.Update, true);
#endif
var rels = await reader.GetWorkbookRelsAsync(excelArchive.EntryCollection, cancellationToken).ConfigureAwait(false);
var rels = await OpenXmlReader.GetWorkbookRelsAsync(excelArchive.EntryCollection, cancellationToken).ConfigureAwait(false);
var sheetEntries = rels?.ToList() ?? [];

// Group images by sheet
Expand Down
Loading
Loading