Skip to content

Commit 595a3b6

Browse files
authored
Merge pull request #30 from akankainen/Issue29
Adds possibility to treat missing fields as null instead of throwing a MissingFieldException
2 parents 0d07f44 + 3dbbe2e commit 595a3b6

File tree

4 files changed

+104
-57
lines changed

4 files changed

+104
-57
lines changed

Frends.Csv.Tests/Tests.cs

Lines changed: 73 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
using Newtonsoft.Json.Linq;
55
using NUnit.Framework;
66

7-
87
namespace Frends.Csv.Tests
98
{
109
[TestFixture]
@@ -18,18 +17,19 @@ public void TestParseSkipRowsWithAutomaticHeaders()
1817
year;car;mark;price
1918
1997;Ford;E350;2,34
2019
2000;Mercury;Cougar;2,38";
21-
var result = Csv.Parse(new ParseInput() {
20+
var result = Csv.Parse(new ParseInput()
21+
{
2222
ColumnSpecifications = new ColumnSpecification[0],
2323
Delimiter = ";",
2424
Csv = csv
25-
}, new ParseOption() {ContainsHeaderRow = true, SkipRowsFromTop = 2, SkipEmptyRows = false});
25+
}, new ParseOption() { ContainsHeaderRow = true, SkipRowsFromTop = 2, SkipEmptyRows = false });
2626

2727
dynamic resultJArray = result.ToJson();
2828
var resultXml = result.ToXml();
2929
var resultData = result.Data;
3030
Assert.That(resultData.Count, Is.EqualTo(2));
3131
Assert.That(resultJArray.Count, Is.EqualTo(2));
32-
Assert.That(resultXml,Does.Contain("<year>2000</year>"));
32+
Assert.That(resultXml, Does.Contain("<year>2000</year>"));
3333
Assert.That(resultJArray[0].price.ToString(), Is.EqualTo("2,34"));
3434
}
3535

@@ -41,20 +41,21 @@ public void TestParseWithColumnSpecAndMissingHeader()
4141

4242
var result = Csv.Parse(new ParseInput()
4343
{
44-
ColumnSpecifications = new[]
44+
ColumnSpecifications = new[]
4545
{
4646
new ColumnSpecification() {Name = "Year", Type = ColumnType.Int},
4747
new ColumnSpecification() {Name = "Car", Type = ColumnType.String},
4848
new ColumnSpecification() {Name = "Mark", Type = ColumnType.String},
4949
new ColumnSpecification() {Name = "Price", Type = ColumnType.Decimal}
5050
},
51-
Delimiter = ";", Csv = csv
52-
}, new ParseOption() { ContainsHeaderRow = false, CultureInfo = "fi-FI"});
51+
Delimiter = ";",
52+
Csv = csv
53+
}, new ParseOption() { ContainsHeaderRow = false, CultureInfo = "fi-FI" });
5354
var resultJArray = result.ToJson() as JArray;
5455
var resultXml = result.ToXml();
5556
var resultData = result.Data;
5657
Assert.That(resultData.Count, Is.EqualTo(2));
57-
Assert.That(resultJArray.Count,Is.EqualTo(2));
58+
Assert.That(resultJArray.Count, Is.EqualTo(2));
5859
Assert.That(resultXml, Does.Contain("<Year>2000</Year>"));
5960
Assert.That(resultJArray[0]["Price"].Value<decimal>(), Is.EqualTo(2.34));
6061
}
@@ -105,14 +106,14 @@ public void TestParseWillAllKindOfDataTypes()
105106
Delimiter = ";",
106107
Csv = csv
107108
}, new ParseOption() { ContainsHeaderRow = true, CultureInfo = "fi-FI" });
108-
var resultJson = (JArray) result.ToJson();
109+
var resultJson = (JArray)result.ToJson();
109110
Assert.That(resultJson[0]["Long"].Value<long>(), Is.EqualTo(4294967296));
110111
var resultXml = result.ToXml();
111112
Assert.That(resultXml, Does.Contain("<DateTime2>1.5.2008 10.34.42</DateTime2>"));
112113
var resultData = result.Data;
113114
var itemArray = resultData[0];
114115
Assert.That(itemArray[0].GetType(), Is.EqualTo(typeof(int)));
115-
Assert.That(itemArray[0],Is.EqualTo(1997));
116+
Assert.That(itemArray[0], Is.EqualTo(1997));
116117

117118
Assert.That(itemArray[1].GetType(), Is.EqualTo(typeof(string)));
118119
Assert.That(itemArray[1], Is.EqualTo("Fo;rd"));
@@ -133,13 +134,67 @@ public void TestParseWillAllKindOfDataTypes()
133134
Assert.That(itemArray[6], Is.EqualTo('f'));
134135

135136
Assert.That(itemArray[7].GetType(), Is.EqualTo(typeof(DateTime)));
136-
Assert.That(itemArray[7], Is.EqualTo(new DateTime(2008,9,15)));
137+
Assert.That(itemArray[7], Is.EqualTo(new DateTime(2008, 9, 15)));
137138

138139
Assert.That(itemArray[8].GetType(), Is.EqualTo(typeof(DateTime)));
139-
Assert.That(itemArray[8], Is.EqualTo(new DateTime(2008,5,1,10,34,42)));
140+
Assert.That(itemArray[8], Is.EqualTo(new DateTime(2008, 5, 1, 10, 34, 42)));
141+
}
142+
143+
[Test]
144+
public void TestParseTreatMissingFieldsAsNullSetToTrue()
145+
{
146+
var csv =
147+
@"header1,header2,header3
148+
value1,value2,value3
149+
value1,value2,value3
150+
value1,value2";
151+
152+
var result = Csv.Parse(new ParseInput()
153+
{
154+
ColumnSpecifications = new ColumnSpecification[0],
155+
Delimiter = ",",
156+
Csv = csv
157+
}, new ParseOption() { ContainsHeaderRow = true, CultureInfo = "fi-FI", TreatMissingFieldsAsNulls = true });
158+
var resultJson = (JArray)result.ToJson();
159+
Assert.That(resultJson[2].Value<string>("header3"), Is.EqualTo(null));
160+
161+
var resultXml = result.ToXml();
162+
Assert.That(resultXml, Does.Contain("<header3 />"));
163+
164+
var resultData = result.Data;
165+
var nullItem = resultData[2][2];
166+
167+
Assert.That(nullItem, Is.EqualTo(null));
140168
}
141169

170+
[Test]
171+
public void TestParseTreatMissingFieldsAsNullSetToFalse()
172+
{
173+
Assert.Throws<CsvHelper.MissingFieldException>(
174+
delegate
175+
{
176+
var csv =
177+
@"header1,header2,header3
178+
value1,value2,value3
179+
value1,value2,value3
180+
value1,value2";
181+
182+
var result = Csv.Parse(new ParseInput()
183+
{
184+
ColumnSpecifications = new ColumnSpecification[0],
185+
Delimiter = ",",
186+
Csv = csv
187+
}, new ParseOption() { ContainsHeaderRow = true, CultureInfo = "fi-FI", TreatMissingFieldsAsNulls = false });
188+
});
189+
}
190+
191+
[Test]
192+
public void TestParseTreatMissingFieldsAsNullDefaultValue()
193+
{
194+
var options = new ParseOption();
142195

196+
Assert.That(options.TreatMissingFieldsAsNulls, Is.EqualTo(false));
197+
}
143198

144199
[Test]
145200
public void TestWriteFromListTable()
@@ -161,7 +216,7 @@ public void TestWriteFromListTable()
161216
new List<object>() {100, "Dilantin", "Melanie", date}
162217
};
163218

164-
var result = Csv.Create(new CreateInput() { InputType = CreateInputType.List, Delimiter = ";", Data = data, Headers = headers}, new CreateOption() { CultureInfo = "fi-FI" });
219+
var result = Csv.Create(new CreateInput() { InputType = CreateInputType.List, Delimiter = ";", Data = data, Headers = headers }, new CreateOption() { CultureInfo = "fi-FI" });
165220
Assert.That(result.Csv,
166221
Is.EqualTo(
167222
@"Dosage;Drug;Patient;Date
@@ -173,12 +228,11 @@ public void TestWriteFromListTable()
173228
"));
174229
}
175230

176-
177231
[Test]
178232
public void TestWriteFromJson()
179233
{
180234
var json = @"[{""cool"":""nice"", ""what"": ""no""}, {""cool"":""not"", ""what"": ""yes""}, {""cool"":""maybe"", ""what"": ""never""}]";
181-
var result = Csv.Create(new CreateInput() { InputType = CreateInputType.Json, Delimiter = ";", Json = json}, new CreateOption());
235+
var result = Csv.Create(new CreateInput() { InputType = CreateInputType.Json, Delimiter = ";", Json = json }, new CreateOption());
182236
Assert.That(result.Csv,
183237
Is.EqualTo(@"cool;what
184238
nice;no
@@ -211,19 +265,11 @@ public void TestNoQuotesOption()
211265
"" Normally I would have quotes "";I would not
212266
"));
213267

214-
215268
var result1 = Csv.Create(new CreateInput() { InputType = CreateInputType.Json, Delimiter = ";", Json = json }, new CreateOption() { NeverAddQuotesAroundValues = true });
216269
Assert.That(result1.Csv,
217270
Is.EqualTo(@"foo;bar
218271
Normally I would have quotes ;I would not
219272
"));
220-
221-
222-
223-
224-
225-
226-
227273
}
228274

229275
[Test]
@@ -254,10 +300,11 @@ public void TestDecimalValues()
254300
0.1;1.00;0.000000000000000000000000000000000000000000000000000000001
255301
"));
256302
}
303+
257304
[Test]
258305
public void ParseAndWriteShouldUseSeparateCultures()
259306
{
260-
var csv =
307+
var csv =
261308
@"First; Second; Number; Date
262309
Foo; bar; 100; 2000-01-01";
263310

@@ -272,7 +319,7 @@ public void ParseAndWriteShouldUseSeparateCultures()
272319
},
273320
Delimiter = ";",
274321
Csv = csv
275-
}, new ParseOption() { ContainsHeaderRow = true, CultureInfo = "en-us"});
322+
}, new ParseOption() { ContainsHeaderRow = true, CultureInfo = "en-us" });
276323

277324
var result = Csv.Create(new CreateInput() { InputType = CreateInputType.List, Delimiter = ";", Data = parseResult.Data, Headers = parseResult.Headers }, new CreateOption() { CultureInfo = "fi-FI" });
278325

@@ -305,7 +352,6 @@ public void TestParseRowsWithAutomaticHeadersWhiteSpaceRemovalDefault()
305352
Assert.That(resultJArray[0].price.ToString(), Is.EqualTo("2,34"));
306353
}
307354

308-
309355
[Test]
310356
public void TestParseRowsWithAutomaticHeadersWhiteSpaceRemovalGiven()
311357
{
@@ -328,9 +374,5 @@ public void TestParseRowsWithAutomaticHeadersWhiteSpaceRemovalGiven()
328374
Assert.That(resultXml, Does.Contain("<year_of_the_z>"));
329375
Assert.That(resultJArray[0].price.ToString(), Is.EqualTo("2,34"));
330376
}
331-
332-
333-
334377
}
335-
}
336-
378+
}

Frends.Csv/Csv.cs

Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,26 +14,28 @@ namespace Frends.Csv
1414
{
1515
public class Csv
1616
{
17-
18-
1917
/// <summary>
2018
/// Parse string csv content to a object. See https://github.com/FrendsPlatform/Frends.Csv
2119
/// </summary>
2220
/// <returns>Object { List&lt;List&lt;object&gt;&gt; Data, List&lt;string&gt; Headers, JToken ToJson(), string ToXml() } </returns>
2321
public static ParseResult Parse([PropertyTab] ParseInput input, [PropertyTab] ParseOption option)
2422
{
25-
26-
2723
var configuration = new CsvConfiguration(new CultureInfo(option.CultureInfo))
2824
{
2925
HasHeaderRecord = option.ContainsHeaderRow,
3026
Delimiter = input.Delimiter,
31-
TrimOptions = option.TrimOutput ? TrimOptions.None : TrimOptions.Trim,
27+
TrimOptions = option.TrimOutput ? TrimOptions.None : TrimOptions.Trim,
3228
IgnoreBlankLines = option.SkipEmptyRows
3329
};
34-
3530

31+
// Setting the MissingFieldFound -delegate property of configuration to null when
32+
// option.TreatMissingFieldsAsNulls is set to true for returning null values for missing fields.
33+
// Otherwise the default setting which throws a MissingFieldException is used
3634

35+
if (option.TreatMissingFieldsAsNulls)
36+
{
37+
configuration.MissingFieldFound = null;
38+
}
3739

3840
using (TextReader sr = new StringReader(input.Csv))
3941
{
@@ -45,12 +47,10 @@ public static ParseResult Parse([PropertyTab] ParseInput input, [PropertyTab] Pa
4547

4648
using (var csvReader = new CsvReader(sr, configuration))
4749
{
48-
49-
5050
if (option.ContainsHeaderRow)
51-
{
52-
csvReader.Read();
53-
csvReader.ReadHeader();
51+
{
52+
csvReader.Read();
53+
csvReader.ReadHeader();
5454
}
5555
var resultData = new List<List<object>>();
5656
var headers = new List<string>();
@@ -87,8 +87,6 @@ public static ParseResult Parse([PropertyTab] ParseInput input, [PropertyTab] Pa
8787
headers = csvReader.HeaderRecord.Select(x => x.Replace(" ", option.ReplaceHeaderWhitespaceWith)).ToList();
8888
}
8989

90-
91-
9290
while (csvReader.Read())
9391
{
9492
var innerList = new List<object>();
@@ -122,7 +120,6 @@ public static ParseResult Parse([PropertyTab] ParseInput input, [PropertyTab] Pa
122120
}
123121

124122
return new ParseResult(resultData, headers, configuration.CultureInfo);
125-
126123
}
127124
}
128125
}
@@ -153,17 +150,16 @@ public static CreateResult Create([PropertyTab] CreateInput input, [PropertyTab]
153150
case CreateInputType.List:
154151
csv = ListToCsvString(input.Data, input.Headers, config, option);
155152
break;
153+
156154
case CreateInputType.Json:
157155
csv = JsonToCsvString(input.Json, config, option);
158156
break;
159157
}
160158
return new CreateResult(csv);
161-
162159
}
163160

164161
private static string ListToCsvString(List<List<object>> inputData, List<string> inputHeaders, CsvConfiguration config, CreateOption option)
165162
{
166-
167163
using (var csvString = new StringWriter())
168164
using (var csv = new CsvWriter(csvString, config))
169165
{
@@ -189,7 +185,6 @@ private static string ListToCsvString(List<List<object>> inputData, List<string>
189185
}
190186
}
191187

192-
193188
private static string JsonToCsvString(string json, CsvConfiguration config, CreateOption option)
194189
{
195190
List<Dictionary<string, string>> data = JsonConvert.DeserializeObject<List<Dictionary<string, string>>>(json);
@@ -218,6 +213,5 @@ private static string JsonToCsvString(string json, CsvConfiguration config, Crea
218213
return csvString.ToString();
219214
}
220215
}
221-
222216
}
223-
}
217+
}

0 commit comments

Comments
 (0)