'CsvHelper write mapper one property to multiple columns

I'm using csvHelper (version 2.8.4) to write a class to csv. My class looks like this:

public class classA
{
   public int Amount { get; set; }
   public Dictionary<string, string> Dict{ get; set; }
}

Is it possible to write a mapper that maps Dict property to multiple columns? using some sort of converter?

for example if the class has the values:
Amount = 15
Dict = new Dictionary<string,string>{["a1"] = "a2",["b1"] = "b2"}

I want the resulting csv to be:

Amount,a1,b1
15,a2,b2

Thanks!



Solution 1:[1]

As mentioned in linked question, you may use ExpandoObject to serialize dictionary.

The following code will work for writing to CSV only, it's converting classA objects to ExpandoObject during serialization, including Amount property which is added manually.

public static List<dynamic> ToExpandoObjects(IReadOnlyList<classA> aObjects)
{
    var allKeys = aObjects
        .SelectMany(a => a.Dict.Keys)
        .Distinct()
        .ToHashSet();

    var result = new List<dynamic>();

    foreach (var a in aObjects)
    {
        var asExpando = new ExpandoObject();
        var asDictionary = (IDictionary<string, object>)asExpando;

        asDictionary[nameof(classA.Amount)] = a.Amount;
        foreach (var key in allKeys)
        {
            if(a.Dict.TryGetValue(key, out var value))
                asDictionary[key] = value;
            else
                asDictionary[key] = null;
        }

        result.Add(asExpando);
    }

    return result;
}

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

E.g. called as:

var records = new[] {
    new classA
    {
        Amount = 15,
        Dict = new Dictionary<string,string>{["a1"] = "a2",["b1"] = "b2"}
    },
    new classA
    {
        Amount = 15,
        Dict = new Dictionary<string,string>{["c1"] = "c2",["b1"] = "b2"}
    }
};

StringBuilder sb = new StringBuilder();
using (var writer = new StringWriter(sb))
using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
{
    csv.WriteRecords(ToExpandoObjects(records));
}

Console.WriteLine(sb.ToString());

produces

Amount a1 b1 c1
15 a2 b2
15 b2 c2

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Renat