diff --git a/src/MiniExcel.OpenXml/Templates/OpenXmlTemplate.Impl.cs b/src/MiniExcel.OpenXml/Templates/OpenXmlTemplate.Impl.cs index e021acb0..6eca0286 100644 --- a/src/MiniExcel.OpenXml/Templates/OpenXmlTemplate.Impl.cs +++ b/src/MiniExcel.OpenXml/Templates/OpenXmlTemplate.Impl.cs @@ -526,7 +526,7 @@ await writer.WriteAsync($"<{prefix}sheetData>" ProcessFormulas(rowXml, newRowIndex); await writer.WriteAsync(CleanXml(rowXml, endPrefix).ToString() #if NET5_0_OR_GREATER - .AsMemory(), cancellationToken + .AsMemory(), cancellationToken #endif ).ConfigureAwait(false); @@ -549,7 +549,7 @@ await writer.WriteAsync(CleanXml(rowXml, endPrefix).ToString() await writer.WriteAsync($"" #if NET7_0_OR_GREATER - .AsMemory(), cancellationToken + .AsMemory(), cancellationToken #endif ).ConfigureAwait(false); @@ -628,10 +628,20 @@ class GenerateCellValuesContext //todo: refactor in a way that needs less parameters [CreateSyncVersion] - private async Task GenerateCellValuesAsync(GenerateCellValuesContext generateCellValuesContext, string endPrefix, StreamWriter writer, - StringBuilder rowXml, int mergeRowCount, bool isHeaderRow, - XRowInfo rowInfo, XmlElement row, int groupingRowDiff, - string innerXml, StringBuilder outerXmlOpen, XmlElement rowElement, CancellationToken cancellationToken = default) + private async Task GenerateCellValuesAsync( + GenerateCellValuesContext generateCellValuesContext, + string endPrefix, + StreamWriter writer, + StringBuilder rowXml, + int mergeRowCount, + bool isHeaderRow, + XRowInfo rowInfo, + XmlElement row, + int groupingRowDiff, + string innerXml, + StringBuilder outerXmlOpen, + XmlElement rowElement, + CancellationToken cancellationToken = default) { var rowIndexDiff = generateCellValuesContext.rowIndexDiff; var headerDiff = generateCellValuesContext.headerDiff; @@ -698,27 +708,24 @@ private async Task GenerateCellValuesAsync(GenerateCe .Replace(")", "") .Split(' '); - object value; + object? value; if (rowInfo.IsDictionary) { - value = dict[newLines[0]]; + value = dict![newLines[0]]; } else if (rowInfo.IsDataTable) { - value = dataRow[newLines[0]]; + value = dataRow![newLines[0]]; } else { - value = string.Empty; var prop = rowInfo.PropsMap[newLines[0]]; - if (prop.PropertyInfoOrFieldInfo == PropertyInfoOrFieldInfo.PropertyInfo) + value = prop.PropertyInfoOrFieldInfo switch { - value = prop.PropertyInfo.GetValue(item); - } - else if (prop.PropertyInfoOrFieldInfo == PropertyInfoOrFieldInfo.FieldInfo) - { - value = prop.FieldInfo.GetValue(item); - } + PropertyInfoOrFieldInfo.PropertyInfo => prop.PropertyInfo.GetValue(item), + PropertyInfoOrFieldInfo.FieldInfo => prop.FieldInfo.GetValue(item), + _ => string.Empty + }; } var evaluation = EvaluateStatement(value, newLines[1], newLines[2]); @@ -750,7 +757,7 @@ private async Task GenerateCellValuesAsync(GenerateCe { var replacements = new Dictionary(); #if NETCOREAPP3_0_OR_GREATER - string MatchDelegate(Match x) => CollectionExtensions.GetValueOrDefault(replacements, x.Groups[1].Value, ""); + string MatchDelegate(Match x) => replacements.GetValueOrDefault(x.Groups[1].Value, ""); #else string MatchDelegate(Match x) => replacements.TryGetValue(x.Groups[1].Value, out var repl) ? repl : ""; #endif @@ -763,12 +770,12 @@ private async Task GenerateCellValuesAsync(GenerateCe object? cellValue; if (rowInfo.IsDictionary) { - if (!dict.TryGetValue(prop.Key, out cellValue)) + if (!dict!.TryGetValue(prop.Key, out cellValue)) continue; } else if (rowInfo.IsDataTable) { - cellValue = dataRow[prop.Key]; + cellValue = dataRow![prop.Key]; } else { @@ -782,7 +789,7 @@ private async Task GenerateCellValuesAsync(GenerateCe ? prop.Value.UnderlyingTypePropType : Nullable.GetUnderlyingType(propInfo.PropertyType) ?? propInfo.PropertyType; - string cellValueStr; + string? cellValueStr; if (type == typeof(bool)) { cellValueStr = (bool)cellValue ? "1" : "0"; @@ -791,7 +798,7 @@ private async Task GenerateCellValuesAsync(GenerateCe { cellValueStr = ConvertToDateTimeString(propInfo, cellValue); } - else if (type?.IsEnum ?? false) + else if (type?.IsEnum is true) { var description = CustomPropertyHelper.GetDescriptionAttribute(type, cellValue); cellValueStr = XmlHelper.EncodeXml(description); @@ -799,15 +806,21 @@ private async Task GenerateCellValuesAsync(GenerateCe else { cellValueStr = XmlHelper.EncodeXml(cellValue?.ToString()); - if (!isDictOrTable && TypeHelper.IsNumericType(type)) + if (TypeHelper.IsNumericType(type)) { if (decimal.TryParse(cellValueStr, out var decimalValue)) cellValueStr = decimalValue.ToString(CultureInfo.InvariantCulture); } } - replacements[key] = cellValueStr; - rowXml.Replace($"@header{{{{{key}}}}}", cellValueStr); + // escaping formulas + var tempReplacement = cellValueStr ?? ""; + var replacementValue = tempReplacement.StartsWith("$=") || tempReplacement.StartsWith("=") + ? $"'{tempReplacement}" + : tempReplacement; + + replacements[key] = replacementValue; + rowXml.Replace($"@header{{{{{key}}}}}", replacementValue); if (isHeaderRow && row.InnerText.Contains(key)) { @@ -915,7 +928,7 @@ await writer.WriteAsync(CleanXml(newRow.OuterXml, endPrefix) } } - return new GenerateCellValuesContext() + return new GenerateCellValuesContext { currentHeader = currentHeader, headerDiff = headerDiff, @@ -1067,8 +1080,8 @@ private void ProcessFormulas(StringBuilder rowXml, int rowIndex) SUM(C2:C7) - */ - var vs = c.SelectNodes("x:v", Ns); + */ + var vs = c.SelectNodes("x:is", Ns); foreach (XmlElement v in vs) { if (!v.InnerText.StartsWith("$=")) diff --git a/tests/MiniExcel.OpenXml.Tests/MiniExcelIssueTests.cs b/tests/MiniExcel.OpenXml.Tests/MiniExcelIssueTests.cs index fd6b7e91..5d051b32 100644 --- a/tests/MiniExcel.OpenXml.Tests/MiniExcelIssueTests.cs +++ b/tests/MiniExcel.OpenXml.Tests/MiniExcelIssueTests.cs @@ -3784,4 +3784,27 @@ public void TestIssue888_ShouldIgnoreFrame() Assert.Equal(dataInSheet, dataRead); } + [Fact] + public void TestIssue915() + { + var templatePath = PathHelper.GetFile("xlsx/TestIssue915.xlsx"); + var value = new Dictionary + { + ["Data"] = new[] + { + new { Name = "Hill", Altitude = 6m }, + new { Name = "Mount", Altitude = 7.4m }, + new { Name = "Peak", Altitude = 8.6m } + } + }; + + using var path = AutoDeletingPath.Create(); + _excelTemplater.ApplyTemplate(path.ToString(), templatePath, value); + + var result = _excelImporter.Query(path.ToString(), true).ToList(); + + Assert.Equal(6, result[0].Altitude); + Assert.Equal(7.4, result[1].Altitude); + Assert.Equal(8.6, result[2].Altitude); + } } \ No newline at end of file diff --git a/tests/data/xlsx/TestIssue915.xlsx b/tests/data/xlsx/TestIssue915.xlsx new file mode 100644 index 00000000..476fd0ef Binary files /dev/null and b/tests/data/xlsx/TestIssue915.xlsx differ