问题
I have a worker with various fields that are fetched from server. I am using CSVHelper package to convert this class to an excel sheet. Worker has Fields like :
class Worker
{
string name;
string phone;
string age;
Dictionary<string,object> customerField;
}
I can map the name, phone, number like
class WorkerMap : CsvClassMap<Worker>
{
public WorkerMap()
{
Map(m => m.name);
Map(m => m.phone);
Map(m => m.age);
}
}
And I generate the map by :
csv.Configuration.RegisterClassMap<WorkerMap>();
Write the list of workers by :
csv.WriteRecords(workerList);
How can I map the customerField dictionary to the excel sheet such that the Key (string) is another column name and the value(object) is the value of the column.
Does CSVHelper help us do it at runtime. I looked through the documentation. Couldn't find anything that worked for me.
回答1:
I don't think that writing a dictionary is supported at this time. For one thing, CsvHelper would have a difficult time knowing what headers to write. Fortunately, it's not too complex to use CsvWriter manually, writing a field at a time. If we assume that each Worker has the same keys in customerField
then your code might look something like this.
var firstWorker = workerList.First();
var keys = firstWorker.customerField.Keys.ToList();
var headers = new []{ "name", "phone", "age"}.Concat(keys).ToList();
var csv = new CsvWriter( textWriter );
// Write the headers
foreach( var header in headers )
{
csv.WriteField(header);
}
csv.NextRecord();
// Write the rows
foreach( var item in workerList)
{
csv.WriteField(item.name);
csv.WriteField(item.phone);
csv.WriteField(item.age);
var dict = worker.customerField;
foreach (var key in keys)
{
csv.WriteField(dict[key]);
}
csv.NextRecord();
}
This code is untested, but should get you pretty close to the behavior you need. If the customerField
dictionary keys are not consistent in the list then the code would be a bit more complicated but it's still solvable.
回答2:
Dictionary isn't supported but ExpandoObject is supported.
https://github.com/JoshClose/CsvHelper/blob/48e70742e06007dae3a635c418b7e3358f667c4f/src/CsvHelper.Tests/Writing/MultipleHeadersTest.cs
https://github.com/JoshClose/CsvHelper/blob/b74a2f95a101158f4cdedd25fae6e8392b58855b/src/CsvHelper.Tests/Writing/DynamicTests.cs
If you follow the first link above you'll find the WriteDynamicHeader method in use on lines 50 & 57.
With help of an extension method I create an ExpandoObject for each record and use CsvHelper to write that object.The Dictionary<string, object>
parameter named document
is what I wish to create the CSV record from.
public static class DictionaryCsvExtentions
{
public static dynamic BuildCsvObject(this Dictionary<string, object> document)
{
dynamic csvObj = new ExpandoObject();
foreach (var p in document)
{
AddProperty(csvObj, p.Key, p.Value);
}
return csvObj;
}
private static void AddProperty(ExpandoObject expando, string propertyName, object propertyValue)
{
var expandoDict = expando as IDictionary<string, object>;
if (expandoDict.ContainsKey(propertyName))
{
expandoDict[propertyName] = propertyValue;
}
else
{
expandoDict.Add(propertyName, propertyValue);
}
}
}
Now I can create an ExpandoObject from my dictionary like this
var csvObj = myDictonary.BuildCsvObject();
and with that, following Josh's tests in the link above we have all we need to use a dictionary fairly seamlessly with CsvHelper. I don't think this is a better solution to Michael's, just a different approach.
credit where credit is due the basic ExpandoObject from dictionary code is from here (where there is a lot more explanation!) https://www.oreilly.com/learning/building-c-objects-dynamically
来源:https://stackoverflow.com/questions/42134421/dynamic-creation-of-columns-using-csvhelper