0

So I got a collection of type Item and I'm trying to map the values to a CSV file but it's not working properly.. It maps these values just fine.. ItemName SubTitle Price Condition Quantity QuantitySold ProductImage and then it doesn't map any more. I think this is because the other two properties are collections.. List and Dictionary

This is how I am currently mapping it.

using (var mem = new MemoryStream())
using (var writer = new StreamWriter(mem))
using (var csvWriter = new CsvWriter(writer, CultureInfo.CurrentCulture))
{
    csvWriter.Configuration.Delimiter = ",";
    csvWriter.Configuration.HasHeaderRecord = true;
    csvWriter.Configuration.AutoMap<Item>();

    csvWriter.WriteHeader<Item>();
    csvWriter.WriteRecords(Products);

    writer.Flush();
    var result = Encoding.UTF8.GetString(mem.ToArray());
    File.WriteAllText("Items.csv", result);
    Console.WriteLine(result);
}

And the issue seems to be that CsvHelper doesn't map dictionaries correctly, which has made this a lot harder. I've read that making it dynamic or something similar could possibly help with that? Because it uses the ExpandoObject? https://stackoverflow.com/a/22997553/11966121

The thing now is that I have no idea how to properly do this, do I map all the fields individually?

Here is the model as well

class Item
{
    public string ItemName { get; set; }
    public string SubTitle { get; set; }
    public string Price { get; set; }
    public string Condition { get; set; }
    public Dictionary<string, List<string>> SKU { get; set; }
    public string Quantity { get; set; }
    public string QuantitySold { get; set; }
    public string ProductImage { get; set; }
    public List<string> ProductImages { get; set; }
}
Riley Varga
  • 670
  • 1
  • 5
  • 15
  • 2
    What do you expect the dictionary and list to look like in a csv file? You have a hierarchical graph but csv is a flat structure. – Crowcoder Jun 11 '20 at 15:35
  • @Crowcoder I just figured it would add all the items to a string, comma seperating each item or something similar – Riley Varga Jun 11 '20 at 15:36
  • 1
    Of course I don't know your requirements, but your data seems more suited toward json or xml than csv. Is that an option or is csv a hard requirement? – Crowcoder Jun 11 '20 at 15:41
  • Unfortunately, csv is a hard requirement – Riley Varga Jun 11 '20 at 15:43
  • If you have collection elements (Lists, Dictionaries) intermixed with records how is anything going to know how to map anything back to the correct place? – Ňɏssa Pøngjǣrdenlarp Jun 11 '20 at 15:44
  • I'm not sure, but I found this documentation, and maybe it could be somewhat useful https://joshclose.github.io/CsvHelper/examples/configuration/class-maps/type-conversion/ – Riley Varga Jun 11 '20 at 15:45
  • Try these articles, I think they might help. https://stackoverflow.com/questions/42134421/dynamic-creation-of-columns-using-csvhelper https://github.com/JoshClose/CsvHelper/issues/719 – schwechel Jun 11 '20 at 15:46

1 Answers1

1

You should be able to use a ClassMap and ConvertUsing to create it.

public class Program
{
    static void Main(string[] args)
    {
        var products = new List<Item>
        {
            new Item
            {
                ItemName = "Item1",
                SubTitle = "Item1 Sub Title",
                Price = "45",
                Condition = "Good",
                SKU = new Dictionary<string, List<string>>
                {
                    { "1234", new List<string>{ "First", "second"} },
                    { "5678", new List<string>{ "third", "fourth"} }
                },
                Quantity = "2",
                QuantitySold = "5",
                ProductImage = "image",
                ProductImages = new List<string>{"Image1", "Image2", "Image3"}
            }
        };

        using (var csvWriter = new CsvWriter(Console.Out, CultureInfo.CurrentCulture))
        {
            csvWriter.Configuration.Delimiter = ",";
            csvWriter.Configuration.RegisterClassMap<ItemClassMap>();

            csvWriter.WriteRecords(products);
        }

        Console.ReadKey();
    }
}

public class Item
{
    public string ItemName { get; set; }
    public string SubTitle { get; set; }
    public string Price { get; set; }
    public string Condition { get; set; }
    public Dictionary<string, List<string>> SKU { get; set; }
    public string Quantity { get; set; }
    public string QuantitySold { get; set; }
    public string ProductImage { get; set; }
    public List<string> ProductImages { get; set; }
}

public class ItemClassMap : ClassMap<Item>
{
    public ItemClassMap()
    {
        Map(m => m.ItemName);
        Map(m => m.SubTitle);
        Map(m => m.Price);
        Map(m => m.Condition);
        Map(m => m.SKU).ConvertUsing(row =>
        {
            var output = string.Empty;
            for (int count = 0; count < row.SKU.Count; count++)
            {
                var kvp = row.SKU.ElementAt(count);
                output += kvp.Key + ":" + string.Join(",", kvp.Value);

                if (count < row.SKU.Count - 1)
                {
                    output += " ";
                }
            }
            return output;
        });
        Map(m => m.Quantity);
        Map(m => m.QuantitySold);
        Map(m => m.ProductImage);
        Map(m => m.ProductImages).ConvertUsing(row => string.Join(",", row.ProductImages));
    }
}
David Specht
  • 7,784
  • 1
  • 22
  • 30
  • Oh wow! That's a really neat solution! The only thing now is that it's not creating a column for each "Key" in the dictionary, but I'm sure that should be easy to fix, looking at the code you provided. – Riley Varga Jun 11 '20 at 18:07
  • Can't seem to figure that one out. – Riley Varga Jun 11 '20 at 19:07
  • That's why @Crowcoder asked you what you expected it to look like. There are a number of unanswered questions about the dictionary. How many keys are there? Does each item have the same keys or can it be different? Does each item have the same number of keys? There is no way of knowing how you want it to look without some more information and is likely going to require a more manual solution. – David Specht Jun 12 '20 at 14:20