From e1e43f24cf9843d6caa80cb1af4eca3a6d877aab Mon Sep 17 00:00:00 2001 From: Riku Virtanen Date: Mon, 9 Oct 2023 14:35:42 +0300 Subject: [PATCH 1/2] Added new parameter ForceQuotesAroundValues --- Frends.Csv.Tests/Tests.cs | 54 +++++++++++++++++++----------------- Frends.Csv/Csv.cs | 8 +++++- Frends.Csv/Definitions.cs | 11 +++++++- Frends.Csv/Frends.Csv.csproj | 2 +- README.md | 1 + 5 files changed, 48 insertions(+), 28 deletions(-) diff --git a/Frends.Csv.Tests/Tests.cs b/Frends.Csv.Tests/Tests.cs index 739ab40..d726610 100644 --- a/Frends.Csv.Tests/Tests.cs +++ b/Frends.Csv.Tests/Tests.cs @@ -215,15 +215,15 @@ public void TestWriteFromListTable() new List() {100, "Dilantin", "Melanie", date} }; - var result = Csv.Create(new CreateInput() { InputType = CreateInputType.List, Delimiter = ";", Data = data, Headers = headers}, new CreateOption() { CultureInfo = "fi-FI" }); - Assert.AreEqual(result.Csv, + var result = Csv.Create(new CreateInput() { InputType = CreateInputType.List, Delimiter = ";", Data = data, Headers = headers}, new CreateOption() { CultureInfo = "fi-FI", ForceQuotesAroundValues = false }); + Assert.AreEqual( @"Dosage;Drug;Patient;Date 25;Indocin;David;1.1.2000 0.00.00 50;Enebrel;Sam;1.1.2000 0.00.00 10;Hydralazine;Christoff;1.1.2000 0.00.00 21;""Combiv;ent"";Janet;1.1.2000 0.00.00 100;Dilantin;Melanie;1.1.2000 0.00.00 -"); +", result.Csv); } [Test] @@ -231,12 +231,12 @@ public void TestWriteFromJson() { var json = @"[{""cool"":""nice"", ""what"": ""no""}, {""cool"":""not"", ""what"": ""yes""}, {""cool"":""maybe"", ""what"": ""never""}]"; var result = Csv.Create(new CreateInput() { InputType = CreateInputType.Json, Delimiter = ";", Json = json}, new CreateOption()); - Assert.AreEqual(result.Csv, + Assert.AreEqual( @"cool;what nice;no not;yes maybe;never -"); +", result.Csv); } [Test] @@ -244,10 +244,21 @@ public void TestNullInputValue() { var json = @"[{""ShouldStayNull"":""null"", ""ShouldBeReplaced"": null}]"; var result = Csv.Create(new CreateInput() { InputType = CreateInputType.Json, Delimiter = ";", Json = json }, new CreateOption() { ReplaceNullsWith = "replacedvalue" }); - Assert.AreEqual(result.Csv, + Assert.AreEqual( @"ShouldStayNull;ShouldBeReplaced null;replacedvalue -"); +", result.Csv); + } + + [Test] + public void TestInputValueWithForceQuotes() + { + var json = @"[{""ShouldStayNull"":""null"", ""ShouldBeReplaced"": null}]"; + var result = Csv.Create(new CreateInput() { InputType = CreateInputType.Json, Delimiter = ";", Json = json }, new CreateOption() { ForceQuotesAroundValues = true }); + Assert.AreEqual( +@"""ShouldStayNull"";""ShouldBeReplaced"" +""null"";"""" +", result.Csv); } [Test] @@ -257,24 +268,17 @@ public void TestNoQuotesOption() ""foo"" : "" Normally I would have quotes "", ""bar"" : ""I would not"" }]"; - var result2 = Csv.Create(new CreateInput() { InputType = CreateInputType.Json, Delimiter = ";", Json = json }, new CreateOption() { NeverAddQuotesAroundValues = false }); - Assert.AreEqual(result2.Csv, + var result2 = Csv.Create(new CreateInput() { InputType = CreateInputType.Json, Delimiter = ";", Json = json }, new CreateOption() { ForceQuotesAroundValues = false, NeverAddQuotesAroundValues = false }); + Assert.AreEqual( @"foo;bar "" Normally I would have quotes "";I would not -"); +", result2.Csv); var result1 = Csv.Create(new CreateInput() { InputType = CreateInputType.Json, Delimiter = ";", Json = json }, new CreateOption() { NeverAddQuotesAroundValues = true }); - Assert.AreEqual(result1.Csv, + Assert.AreEqual( @"foo;bar Normally I would have quotes ;I would not -"); - - - - - - - +", result1.Csv); } [Test] @@ -285,10 +289,10 @@ public void TestDatetimeValue() ""string"" : ""foo"" }]"; var result = Csv.Create(new CreateInput() { InputType = CreateInputType.Json, Delimiter = ";", Json = json }, new CreateOption() { }); - Assert.AreEqual(result.Csv, + Assert.AreEqual( @"datetime;string 2018-11-22T10:30:55;foo -"); +", result.Csv); } [Test] @@ -300,10 +304,10 @@ public void TestDecimalValues() ""baz"" : 0.000000000000000000000000000000000000000000000000000000001 }]"; var result = Csv.Create(new CreateInput() { InputType = CreateInputType.Json, Delimiter = ";", Json = json }, new CreateOption() { }); - Assert.AreEqual(result.Csv, + Assert.AreEqual( @"foo;bar;baz 0.1;1.00;0.000000000000000000000000000000000000000000000000000000001 -"); +", result.Csv); } [Test] @@ -328,10 +332,10 @@ public void ParseAndWriteShouldUseSeparateCultures() var result = Csv.Create(new CreateInput() { InputType = CreateInputType.List, Delimiter = ";", Data = parseResult.Data, Headers = parseResult.Headers }, new CreateOption() { CultureInfo = "fi-FI" }); - Assert.AreEqual(result.Csv, + Assert.AreEqual( @"First;Second;Number;Date Foo;"" bar"";100;1.1.2000 0.00.00 -"); +", result.Csv); } [Test] diff --git a/Frends.Csv/Csv.cs b/Frends.Csv/Csv.cs index f749033..564e6f8 100644 --- a/Frends.Csv/Csv.cs +++ b/Frends.Csv/Csv.cs @@ -154,13 +154,19 @@ public static CreateResult Create([PropertyTab] CreateInput input, [PropertyTab] HasHeaderRecord = option.IncludeHeaderRow }; + if (option.ForceQuotesAroundValues) + { + config.ShouldQuote = (field) => true; + } + if (option.NeverAddQuotesAroundValues) { config.Mode = CsvMode.NoEscape; // if IgnoreQuotes is true, seems like ShouldQuote function has to return false in all cases // if IgnoreQuotes is false ShouldQuote can't have any implementation otherwise it will overwrite IgnoreQuotes statement ( might turn it on again) - config.ShouldQuote = (field) => (!option.NeverAddQuotesAroundValues); + config.ShouldQuote = (field) => false; } + var csv = string.Empty; switch (input.InputType) diff --git a/Frends.Csv/Definitions.cs b/Frends.Csv/Definitions.cs index 69312f7..582f281 100644 --- a/Frends.Csv/Definitions.cs +++ b/Frends.Csv/Definitions.cs @@ -65,13 +65,19 @@ public class CreateOption /// If set true csv's fields are never put in quotes /// [DefaultValue("false")] - public bool NeverAddQuotesAroundValues { get; set; } + public bool NeverAddQuotesAroundValues { get; set; } = false; /// /// Input's null values will be replaced with this value /// [DisplayFormat(DataFormatString = "Text")] public string ReplaceNullsWith { get; set; } + + /// + /// Force quotes to all values + /// + [DefaultValue("false")] + public bool ForceQuotesAroundValues { get; set; } = false; } public class CreateResult @@ -163,6 +169,7 @@ public class ParseOption /// [DefaultValue("false")] public bool TreatMissingFieldsAsNulls { get; set; } = false; + /// /// A flag to let the reader know if reference should be ignored. /// @@ -175,6 +182,8 @@ public class ParseOption [DefaultValue("false")] public bool IgnoreQuotes { get; set; } = false; + + } public class ParseResult diff --git a/Frends.Csv/Frends.Csv.csproj b/Frends.Csv/Frends.Csv.csproj index 766fe07..6419ae2 100644 --- a/Frends.Csv/Frends.Csv.csproj +++ b/Frends.Csv/Frends.Csv.csproj @@ -2,7 +2,7 @@ netstandard2.0;net461 - 1.0.1 + 1.0.0 true HiQ Finland HiQ Finland diff --git a/README.md b/README.md index 64678a3..04cd378 100644 --- a/README.md +++ b/README.md @@ -112,6 +112,7 @@ NOTE: Be sure to merge the latest from "upstream" before making a pull request! | CultureInfo | string | The culture info to write the file with, e.g. for decimal separators. InvariantCulture will be used by default. See list of cultures [here](https://msdn.microsoft.com/en-us/library/ee825488(v=cs.20).aspx); use the Language Culture Name.
NOTE: Due to an issue with the CsvHelpers library, all CSV tasks will use the culture info setting of the first CSV task in the process; you cannot use different cultures for reading and parsing CSV files in the same process. | | NeverAddQuotesAroundValues | bool | If set true csv's fields are never put in quotes | | ReplaceNullsWith | string | Input's null values will be replaced with this value | +| ForceQuotesAroundValues | bool | Force quotes around all values | #### Result From ec8d38f428a9ecda5b4858f292131800c88103f5 Mon Sep 17 00:00:00 2001 From: Riku Virtanen Date: Mon, 9 Oct 2023 16:31:36 +0300 Subject: [PATCH 2/2] PR review changes --- Frends.Csv.Tests/Tests.cs | 80 +++++++++++++++++++-------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/Frends.Csv.Tests/Tests.cs b/Frends.Csv.Tests/Tests.cs index d726610..e2ffa01 100644 --- a/Frends.Csv.Tests/Tests.cs +++ b/Frends.Csv.Tests/Tests.cs @@ -26,10 +26,10 @@ public void TestParseSkipRowsWithAutomaticHeaders() dynamic resultJArray = result.ToJson(); var resultXml = result.ToXml(); var resultData = result.Data; - Assert.AreEqual(resultData.Count, 2); - Assert.AreEqual(resultJArray.Count, 2); + Assert.AreEqual(2, resultData.Count); + Assert.AreEqual(2, resultJArray.Count); Assert.IsTrue(resultXml.Contains("2000")); - Assert.AreEqual(resultJArray[0].price.ToString(), "2,34"); + Assert.AreEqual("2,34", resultJArray[0].price.ToString()); } [Test] @@ -53,10 +53,10 @@ public void TestParseWithColumnSpecAndMissingHeader() var resultJArray = result.ToJson() as JArray; var resultXml = result.ToXml(); var resultData = result.Data; - Assert.AreEqual(resultData.Count, 2); - Assert.AreEqual(resultJArray.Count, 2); + Assert.AreEqual(2, resultData.Count); + Assert.AreEqual(2, resultJArray.Count); Assert.IsTrue(resultXml.Contains("2000")); - Assert.AreEqual(resultJArray[0]["Price"].Value(), 2.34); + Assert.AreEqual(2.34, resultJArray[0]["Price"].Value()); } [Test] @@ -75,10 +75,10 @@ public void TestParseWithNoColumnSpecAndNoHeader() var resultJArray = result.ToJson() as JArray; var resultXml = result.ToXml(); var resultData = result.Data; - Assert.AreEqual(resultData.Count, 2); - Assert.AreEqual(resultJArray.Count, 2); + Assert.AreEqual(2, resultData.Count); + Assert.AreEqual(2, resultJArray.Count); Assert.IsTrue(resultXml.Contains("<0>2000")); - Assert.AreEqual(resultJArray[0]["3"].Value(), "2,34"); + Assert.AreEqual("2,34", resultJArray[0]["3"].Value()); } [Test] @@ -106,37 +106,37 @@ public void TestParseWillAllKindOfDataTypes() Csv = csv }, new ParseOption() { ContainsHeaderRow = true, CultureInfo = "fi-FI", IgnoreReferences = true }); var resultJson = (JArray) result.ToJson(); - Assert.AreEqual(resultJson[0]["Long"].Value(), 4294967296); + Assert.AreEqual(4294967296, resultJson[0]["Long"].Value()); var resultXml = result.ToXml(); Assert.IsTrue(resultXml.Contains("1.5.2008 10.34.42")); var resultData = result.Data; var itemArray = resultData[0]; - Assert.AreEqual(itemArray[0].GetType(), typeof(int)); - Assert.AreEqual(itemArray[0], 1997); + Assert.AreEqual(typeof(int), itemArray[0].GetType()); + Assert.AreEqual(1997, itemArray[0]); - Assert.AreEqual(itemArray[1].GetType(), typeof(string)); - Assert.AreEqual(itemArray[1], "Fo;rd"); + Assert.AreEqual(typeof(string), itemArray[1].GetType()); + Assert.AreEqual("Fo;rd", itemArray[1]); - Assert.AreEqual(itemArray[2].GetType(), typeof(decimal)); - Assert.AreEqual(itemArray[2], 2.34d); + Assert.AreEqual(typeof(decimal), itemArray[2].GetType()); + Assert.AreEqual(2.34d, itemArray[2]); - Assert.AreEqual(itemArray[3].GetType(), typeof(bool)); - Assert.AreEqual(itemArray[3], true); + Assert.AreEqual(typeof(bool), itemArray[3].GetType()); + Assert.AreEqual(true, itemArray[3]); - Assert.AreEqual(itemArray[4].GetType(), typeof(bool)); - Assert.AreEqual(itemArray[4], true); + Assert.AreEqual(typeof(bool), itemArray[4].GetType()); + Assert.AreEqual(true, itemArray[4]); - Assert.AreEqual(itemArray[5].GetType(), typeof(long)); - Assert.AreEqual(itemArray[5], 4294967296); + Assert.AreEqual(typeof(long), itemArray[5].GetType()); + Assert.AreEqual(4294967296, itemArray[5]); - Assert.AreEqual(itemArray[6].GetType(), typeof(char)); - Assert.AreEqual(itemArray[6], 'f'); + Assert.AreEqual(typeof(char), itemArray[6].GetType()); + Assert.AreEqual('f', itemArray[6]); - Assert.AreEqual(itemArray[7].GetType(), typeof(DateTime)); - Assert.AreEqual(itemArray[7], new DateTime(2008, 9, 15)); + Assert.AreEqual(typeof(DateTime), itemArray[7].GetType()); + Assert.AreEqual(new DateTime(2008, 9, 15), itemArray[7]); - Assert.That(itemArray[8].GetType(), Is.EqualTo(typeof(DateTime))); - Assert.That(itemArray[8], Is.EqualTo(new DateTime(2008, 5, 1, 10, 34, 42))); + Assert.AreEqual(typeof(DateTime), itemArray[8].GetType()); + Assert.AreEqual(new DateTime(2008, 5, 1, 10, 34, 42), itemArray[8]); } [Test] @@ -155,7 +155,7 @@ public void TestParseTreatMissingFieldsAsNullSetToTrue() Csv = csv }, new ParseOption() { ContainsHeaderRow = true, CultureInfo = "fi-FI", TreatMissingFieldsAsNulls = true }); var resultJson = (JArray)result.ToJson(); - Assert.AreEqual(resultJson[2].Value("header3"), null); + Assert.AreEqual(null, resultJson[2].Value("header3")); var resultXml = result.ToXml(); Assert.IsTrue(resultXml.Contains("")); @@ -163,7 +163,7 @@ public void TestParseTreatMissingFieldsAsNullSetToTrue() var resultData = result.Data; var nullItem = resultData[2][2]; - Assert.AreEqual(nullItem, null); + Assert.AreEqual(null, nullItem); } [Test] @@ -192,7 +192,7 @@ public void TestParseTreatMissingFieldsAsNullDefaultValue() { var options = new ParseOption(); - Assert.AreEqual(options.TreatMissingFieldsAsNulls, false); + Assert.AreEqual(false, options.TreatMissingFieldsAsNulls); } [Test] @@ -355,10 +355,10 @@ public void TestParseRowsWithAutomaticHeadersWhiteSpaceRemovalDefault() dynamic resultJArray = result.ToJson(); var resultXml = result.ToXml(); var resultData = result.Data; - Assert.AreEqual(resultData.Count, 2); - Assert.AreEqual(resultJArray.Count, 2); + Assert.AreEqual(2, resultData.Count); + Assert.AreEqual(2, resultJArray.Count); Assert.IsTrue(resultXml.Contains("")); - Assert.AreEqual(resultJArray[0].price.ToString(), "2,34"); + Assert.AreEqual("2,34", resultJArray[0].price.ToString()); } [Test] @@ -378,10 +378,10 @@ public void TestParseRowsWithAutomaticHeadersWhiteSpaceRemovalGiven() dynamic resultJArray = result.ToJson(); var resultXml = result.ToXml(); var resultData = result.Data; - Assert.AreEqual(resultData.Count, 2); - Assert.AreEqual(resultJArray.Count, 2); + Assert.AreEqual(2, resultData.Count); + Assert.AreEqual(2, resultJArray.Count); Assert.IsTrue(resultXml.Contains("")); - Assert.AreEqual(resultJArray[0].price.ToString(), "2,34"); + Assert.AreEqual("2,34", resultJArray[0].price.ToString()); } [Test] @@ -398,10 +398,10 @@ public void TestParseIgnoresQuotesInRowData() dynamic resultJArray = result.ToJson(); var resultXml = result.ToXml(); var resultData = result.Data; - Assert.AreEqual(resultData.Count, 2); - Assert.AreEqual(resultJArray.Count, 2); + Assert.AreEqual(2, resultData.Count); + Assert.AreEqual(2, resultJArray.Count); Assert.IsTrue(resultXml.Contains("2000")); - Assert.AreEqual(resultJArray[0].price.ToString(), "2,34"); + Assert.AreEqual("2,34", resultJArray[0].price.ToString()); } }