From 8a7f6cd4e77e3086ff23ac075e1634b9139bd160 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Wimpassinger?= Date: Sun, 28 Sep 2025 03:12:13 +0200 Subject: [PATCH] Constants use the CultureInfo from the CsvConfiguration if provided. --- .../Expressions/ObjectRecordWriter.cs | 10 +- .../CsvHelper.Tests/Writing/ConstantTests.cs | 98 ++++++++++++++++++- 2 files changed, 106 insertions(+), 2 deletions(-) diff --git a/src/CsvHelper/Expressions/ObjectRecordWriter.cs b/src/CsvHelper/Expressions/ObjectRecordWriter.cs index e00119024..027f347e3 100644 --- a/src/CsvHelper/Expressions/ObjectRecordWriter.cs +++ b/src/CsvHelper/Expressions/ObjectRecordWriter.cs @@ -80,6 +80,11 @@ protected override Action CreateWriteDelegate(Type type) { fieldExpression = Expression.Constant(memberMap.Data.Constant); var typeConverterExpression = Expression.Constant(Writer.Context.TypeConverterCache.GetConverter(memberMap.Data.Constant.GetType())); + + memberMap.Data.TypeConverterOptions = TypeConverterOptions.Merge( + new TypeConverterOptions { CultureInfo = Writer.Configuration.CultureInfo }, + memberMap.Data.TypeConverterOptions); + var method = typeof(ITypeConverter).GetMethod(nameof(ITypeConverter.ConvertToString))!; fieldExpression = Expression.Convert(fieldExpression, typeof(object)); fieldExpression = Expression.Call(typeConverterExpression, method, fieldExpression, Expression.Constant(Writer), Expression.Constant(memberMap.Data)); @@ -96,7 +101,10 @@ protected override Action CreateWriteDelegate(Type type) fieldExpression = ExpressionManager.CreateGetMemberExpression(recordParameterConverted, Writer.Context.Maps[type]!, memberMap)!; var typeConverterExpression = Expression.Constant(memberMap.Data.TypeConverter); - memberMap.Data.TypeConverterOptions = TypeConverterOptions.Merge(new TypeConverterOptions { CultureInfo = Writer.Configuration.CultureInfo }, Writer.Context.TypeConverterOptionsCache.GetOptions(memberMap.Data.Member!.MemberType()), memberMap.Data.TypeConverterOptions); + memberMap.Data.TypeConverterOptions = TypeConverterOptions.Merge( + new TypeConverterOptions { CultureInfo = Writer.Configuration.CultureInfo }, + Writer.Context.TypeConverterOptionsCache.GetOptions(memberMap.Data.Member!.MemberType()), + memberMap.Data.TypeConverterOptions); var method = typeof(ITypeConverter).GetMethod(nameof(ITypeConverter.ConvertToString))!; fieldExpression = Expression.Convert(fieldExpression, typeof(object)); diff --git a/tests/CsvHelper.Tests/Writing/ConstantTests.cs b/tests/CsvHelper.Tests/Writing/ConstantTests.cs index cc8087211..be218939b 100644 --- a/tests/CsvHelper.Tests/Writing/ConstantTests.cs +++ b/tests/CsvHelper.Tests/Writing/ConstantTests.cs @@ -11,7 +11,7 @@ namespace CsvHelper.Tests.Writing { - + public class ConstantTests { [Fact] @@ -90,6 +90,79 @@ public void IntConstantTest() } } + [Fact] + public void DoubleConstantTest_CultureInvariant() + { + CultureInfo.CurrentCulture = CultureInfo.InvariantCulture; + var config = new CsvConfiguration(CultureInfo.InvariantCulture) + { + HasHeaderRecord = false, + }; + using (var writer = new StringWriter()) + using (var csv = new CsvWriter(writer, config)) + { + var records = new List + { + new Test { Id = 1, Name = "one" }, + }; + + csv.Context.RegisterClassMap(); + csv.WriteRecords(records); + writer.Flush(); + + Assert.Equal("3.141592,3.1415\r\n", writer.ToString()); + } + } + + [Fact] + public void DoubleConstantTest_CultureDe() + { + CultureInfo.CurrentCulture = CultureInfo.InvariantCulture; + var config = new CsvConfiguration(new CultureInfo("de-DE")) + { + HasHeaderRecord = false, + }; + using (var writer = new StringWriter()) + using (var csv = new CsvWriter(writer, config)) + { + var records = new List + { + new Test { Id = 1, Name = "one" }, + }; + + csv.Context.RegisterClassMap(); + csv.WriteRecords(records); + writer.Flush(); + + Assert.Equal("3,141592;3,1415\r\n", writer.ToString()); + } + } + + [Fact] + public void DoubleConstantTest_CultureDeOverrideInvariant() + { + CultureInfo.CurrentCulture = CultureInfo.InvariantCulture; + var config = new CsvConfiguration(new CultureInfo("de-DE")) + { + HasHeaderRecord = false, + }; + using (var writer = new StringWriter()) + using (var csv = new CsvWriter(writer, config)) + { + var records = new List + { + new Test { Id = 1, Name = "one" }, + }; + + csv.Context.RegisterClassMap(new TestDoublCultureOverrideMap(CultureInfo.InvariantCulture)); + csv.WriteRecords(records); + writer.Flush(); + + Assert.Equal("3,141592;3.1415\r\n", writer.ToString()); + } + } + + private class Test { public int Id { get; set; } @@ -122,5 +195,28 @@ public TestStringMap() Map(m => m.Name).Constant("constant"); } } + + private sealed class TestDoubleMap : ClassMap + { + public TestDoubleMap() + { + Map(m => m.Id).Ignore(); + Map(m => m.Name).Ignore(); + Map().Constant(3.141592); + Map().Constant(3.1415m); + } + } + + private sealed class TestDoublCultureOverrideMap : ClassMap + { + public TestDoublCultureOverrideMap(CultureInfo culture) + { + Map(m => m.Id).Ignore(); + Map(m => m.Name).Ignore(); + Map().Constant(3.141592); + Map().Constant(3.1415m).TypeConverterOption.CultureInfo(culture); + } + } + } }