Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
8 changes: 8 additions & 0 deletions files-csharp/WriteToCsv/WriteToCsv/Contact.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace WritingCSV
{
public class Contact
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
}
}
46 changes: 46 additions & 0 deletions files-csharp/WriteToCsv/WriteToCsv/Employee.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using CsvHelper.Configuration.Attributes;

namespace WritingCSV
{
// The two class-level attributes take effect only through a
// configuration built with the class's attributes, for example
// CsvConfiguration.FromAttributes<Employee>(CultureInfo.InvariantCulture);
// a CsvWriter built from a CultureInfo alone ignores them.
[Delimiter(";")]
[HasHeaderRecord(false)]
public class Employee
{
[Name("Identifier")]
[Index(0)]
public int Id { get; set; }

[Name("Full Name")]
[Index(1)]
public string Name { get; set; } = string.Empty;

[Index(2)]
[Format("yyyy-MM-dd")]
public DateTime Hired { get; set; }

[Index(3)]
[CultureInfo("en-US")]
[Format("C")]
public decimal Salary { get; set; }

[Index(4)]
[BooleanTrueValues("yes")]
[BooleanFalseValues("no")]
public bool Active { get; set; }

[Index(5)]
[Constant("EU")]
public string Region { get; set; } = string.Empty;

[Index(6)]
[NullValues("N/A")]
public string? Nickname { get; set; }

[Ignore]
public string Notes { get; set; } = string.Empty;
}
}
2 changes: 1 addition & 1 deletion files-csharp/WriteToCsv/WriteToCsv/Person.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public class Person
[Index(0)]
public int Id { get; set; }
[Index(2)]
public string Name { get; set; }
public string Name { get; set; } = string.Empty;
[Index(1)]
public bool IsLiving { get; set; }
[Format("yyyy-MM-dd")]
Expand Down
14 changes: 14 additions & 0 deletions files-csharp/WriteToCsv/WriteToCsv/PersonMap.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using CsvHelper.Configuration;

namespace WritingCSV
{
public sealed class PersonMap : ClassMap<Person>
{
public PersonMap()
{
Map(p => p.Id).Name("Identifier").Index(0);
Map(p => p.IsLiving).Index(1);
Map(p => p.Name).Index(2);
}
}
}
107 changes: 107 additions & 0 deletions files-csharp/WriteToCsv/WriteToCsv/WriteMethods.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using CsvHelper;
using CsvHelper.Configuration;
using System;
using System.Collections.Generic;
using System.Globalization;
Expand Down Expand Up @@ -27,5 +28,111 @@ public static void WritePersonsWithDoB()
}

}

public static List<Person> CreatePersons() => new()
{
new Person { Id = 1, IsLiving = true, Name = "John", DateOfBirth = Convert.ToDateTime("03/05/2006") },
new Person { Id = 2, IsLiving = true, Name = "Steve", DateOfBirth = Convert.ToDateTime("03/09/1998") },
new Person { Id = 3, IsLiving = true, Name = "James", DateOfBirth = Convert.ToDateTime("03/08/1994") }
};

public static void WriteWithDefaultSettings(string path = "filePersons.csv")
{
using (var writer = new StreamWriter(path))
using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
{
csv.WriteRecords(CreatePersons());
}
}

public static void WriteWithoutHeader(string path = "filePersons.csv")
{
var configPersons = new CsvConfiguration(CultureInfo.InvariantCulture)
{
HasHeaderRecord = false
};

using (var writer = new StreamWriter(path))
using (var csv = new CsvWriter(writer, configPersons))
{
csv.WriteRecords(CreatePersons());
}
}

public static void WriteWithClassMap(string path = "filePersons.csv")
{
using var writer = new StreamWriter(path);
using var csv = new CsvWriter(writer, CultureInfo.InvariantCulture);

csv.Context.RegisterClassMap<PersonMap>();
csv.WriteRecords(CreatePersons());
}

public static void AppendPersons(string path = "filePersons.csv")
{
var configPersons = new CsvConfiguration(CultureInfo.InvariantCulture)
{
HasHeaderRecord = false
};

using (var stream = File.Open(path, FileMode.Append))
using (var writer = new StreamWriter(stream))
using (var csv = new CsvWriter(writer, configPersons))
{
csv.WriteRecords(CreatePersons());
}
}

public static string WriteTrickyValues()
{
var trickyContacts = new List<Contact>()
{
new Contact { Id = 1, Name = "Doe, John" },
new Contact { Id = 2, Name = "He said \"hi\"" },
new Contact { Id = 3, Name = "line1\nline2" }
};

using var writer = new StringWriter();
using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
{
csv.WriteRecords(trickyContacts);
}

return writer.ToString();
}

public static async Task WritePersonsAsync(string path = "filePersons.csv")
{
await using var writer = new StreamWriter(path);
await using var csv = new CsvWriter(writer, CultureInfo.InvariantCulture);

await csv.WriteRecordsAsync(CreatePersons());
}

public static string WriteEmployees(IWriterConfiguration configuration)
{
var employees = new List<Employee>()
{
new Employee
{
Id = 1,
Name = "Doe, John",
Hired = new DateTime(2020, 3, 5),
Salary = 1234.50m,
Active = true,
Region = "US",
Nickname = null,
Notes = "not written"
}
};

using var writer = new StringWriter();
using (var csv = new CsvWriter(writer, configuration))
{
csv.WriteRecords(employees);
}

return writer.ToString();
}
}
}
4 changes: 2 additions & 2 deletions files-csharp/WriteToCsv/WriteToCsv/WriteToCsv.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="CsvHelper" Version="27.2.1" />
<PackageReference Include="CsvHelper" Version="33.1.0" />
</ItemGroup>

</Project>
142 changes: 142 additions & 0 deletions files-csharp/WriteToCsv/WriteToCsvTests/WriteMethodsTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
using System;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using CsvHelper.Configuration;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using WritingCSV;

namespace WriteToCsvTests
{
[TestClass]
public class WriteMethodsTests
{
private static readonly string[] DataRows =
{
"1,True,John,2006-03-05",
"2,True,Steve,1998-03-09",
"3,True,James,1994-03-08"
};

private static string NewTempPath() =>
Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());

[TestMethod]
public void GivenPersons_WhenWritingWithDefaultSettings_ThenHeaderComesFromTheClass()
{
var path = NewTempPath();
try
{
WriteMethods.WriteWithDefaultSettings(path);

var expected = "Identifier,IsLiving,Name,DateOfBirth\r\n" + string.Join("\r\n", DataRows) + "\r\n";
Assert.AreEqual(expected, File.ReadAllText(path));
}
finally
{
File.Delete(path);
}
}

[TestMethod]
public void GivenNoHeaderConfig_WhenWriting_ThenFirstLineIsData()
{
var path = NewTempPath();
try
{
WriteMethods.WriteWithoutHeader(path);

CollectionAssert.AreEqual(DataRows, File.ReadAllLines(path));
}
finally
{
File.Delete(path);
}
}

[TestMethod]
public void GivenClassMap_WhenWriting_ThenHeaderAndOrderFollowTheMap()
{
var path = NewTempPath();
try
{
WriteMethods.WriteWithClassMap(path);

var expected = "Identifier,IsLiving,Name\r\n1,True,John\r\n2,True,Steve\r\n3,True,James\r\n";
Assert.AreEqual(expected, File.ReadAllText(path));
}
finally
{
File.Delete(path);
}
}

[TestMethod]
public void GivenAFileWithoutHeader_WhenAppending_ThenRowsAreAddedAtTheEnd()
{
var path = NewTempPath();
try
{
WriteMethods.WriteWithoutHeader(path);
WriteMethods.AppendPersons(path);

CollectionAssert.AreEqual(DataRows.Concat(DataRows).ToArray(), File.ReadAllLines(path));
}
finally
{
File.Delete(path);
}
}

[TestMethod]
public void GivenTrickyValues_WhenWriting_ThenFieldsAreQuotedAndQuotesDoubled()
{
var expected = "Id,Name\r\n"
+ "1,\"Doe, John\"\r\n"
+ "2,\"He said \"\"hi\"\"\"\r\n"
+ "3,\"line1\nline2\"\r\n";

Assert.AreEqual(expected, WriteMethods.WriteTrickyValues());
}

[TestMethod]
public async Task GivenPersons_WhenWritingAsync_ThenFileMatchesTheSynchronousWrite()
{
var syncPath = NewTempPath();
var asyncPath = NewTempPath();
try
{
WriteMethods.WriteWithDefaultSettings(syncPath);
await WriteMethods.WritePersonsAsync(asyncPath);

CollectionAssert.AreEqual(File.ReadAllBytes(syncPath), File.ReadAllBytes(asyncPath));
}
finally
{
File.Delete(syncPath);
File.Delete(asyncPath);
}
}

[TestMethod]
public void GivenAttributedEmployee_WhenWritingWithACulture_ThenClassLevelAttributesAreIgnored()
{
var csv = WriteMethods.WriteEmployees(new CsvConfiguration(CultureInfo.InvariantCulture));

var expected = "Identifier,Full Name,Hired,Salary,Active,Region,Nickname\r\n"
+ "1,\"Doe, John\",2020-03-05,\"$1,234.50\",yes,EU,N/A\r\n";
Assert.AreEqual(expected, csv);
}

[TestMethod]
public void GivenAttributedEmployee_WhenWritingFromAttributes_ThenClassLevelAttributesApply()
{
var configuration = CsvConfiguration.FromAttributes<Employee>(CultureInfo.InvariantCulture);

var csv = WriteMethods.WriteEmployees(configuration);

Assert.AreEqual("1;Doe, John;2020-03-05;$1,234.50;yes;EU;N/A\r\n", csv);
}
}
}
12 changes: 6 additions & 6 deletions files-csharp/WriteToCsv/WriteToCsvTests/WriteToCsvTests.csproj
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>

<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="CsvHelper" Version="27.2.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
<PackageReference Include="MSTest.TestAdapter" Version="2.2.7" />
<PackageReference Include="MSTest.TestFramework" Version="2.2.7" />
<PackageReference Include="coverlet.collector" Version="3.1.0" />
<PackageReference Include="CsvHelper" Version="33.1.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.10.1" />
<PackageReference Include="MSTest.TestAdapter" Version="4.5.1" />
<PackageReference Include="MSTest.TestFramework" Version="4.5.1" />
<PackageReference Include="coverlet.collector" Version="10.1.0" />
</ItemGroup>

<ItemGroup>
Expand Down
Loading