I am Using csvhelper.
I have that List of dynamic objects (dynamic) that I want to write as csv but I cannot find how to specify the column order. I need the csv columns to be in a specific order. I could not use a ClassMap since it requires a type which I don't have since using dynamic. Thx
// Requirements:
// input file structure is like: Id, TradeId, Date, Value1, value2
// need to remove TradeId, Date and write like in that order : value2, Id, value1
var fileName = @"whatever path";
var configuration = new CsvConfiguration(CultureInfo.InvariantCulture)
{
Encoding = Encoding.UTF8, // Our file uses UTF-8 encoding.
Delimiter = "," // The delimiter is a comma.
};
// reading input file, removing 2 columns TradeId and Date
List<dynamic> records;
using (var fs = File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.Read))
{
using (var textReader = new StreamReader(fs, Encoding.UTF8))
using (var csv = new CsvReader(textReader, configuration))
{
records = csv.GetRecords<dynamic>().ToList();
foreach (var record in records)
{
var dict = (IDictionary<string, object>)record;
dict.Remove("TradeId");
dict.Remove("Date");
}
}
}
// need to write value2, Id, value1
CsvConfiguration conf = new CsvConfiguration(CultureInfo.InvariantCulture);
conf.HasHeaderRecord = false; // do not write headers
using (var writer = new StreamWriter("whatever output file name"))
using (var csvw = new CsvWriter(writer, conf))
{
csvw.WriteRecords(records);
}
There was a discussion about this in 2018 and at that time @JoshClose advised regarding dynamic objects:
It's because
ExpandoObject
uses aDictionary<string, object>
underneath to store the data. To give the user the ability write different types on each row, I can't do any sort of ordering based on a particular record.
He did, however, at that time create a DynamicPropertySort
configuration, which for this simple example you could use.
CsvConfiguration conf = new CsvConfiguration(CultureInfo.InvariantCulture)
{
HasHeaderRecord = false,
DynamicPropertySort = Comparer<string>.Create((x, y) =>
{
if (x == "Value2" || y == "Value1")
{
return -1;
}
return 1;
})
};
For anything more complex, your best bet will be to manually write out the fields.
void Main()
{
var input = new StringBuilder();
input.AppendLine("Id,TradeId,Date,Value1,Value2");
input.AppendLine("1,11,2/28/2023,value1,value2");
input.AppendLine("2,12,2/28/2022,value21,value22");
using var reader = new StringReader(input.ToString());
using var csvReader = new CsvReader(reader, CultureInfo.InvariantCulture);
var records = csvReader.GetRecords<dynamic>().ToList();
using var csvWriter = new CsvWriter(Console.Out, CultureInfo.InvariantCulture);
foreach (var record in records)
{
csvWriter.WriteField(record.Value2);
csvWriter.WriteField(record.Id);
csvWriter.WriteField(record.Value1);
csvWriter.NextRecord();
}
}