Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Write a CSV file with a dictionary field as separate column headers #2160

Open
nfplee opened this issue May 17, 2023 · 3 comments
Open

Write a CSV file with a dictionary field as separate column headers #2160

nfplee opened this issue May 17, 2023 · 3 comments
Labels

Comments

@nfplee
Copy link

nfplee commented May 17, 2023

Is it possible to create a CSV file which has a dictionary collection, where each key in the dictionary is mapped as separate column headers. This is the best I've come up with so far:

public class ProductViewModel {
    public int Id { get; set; }
    public string Name { get; set; }
    public IDictionary<string, object?> Attributes { get; set; }
}

public class ProductViewModelMap : ClassMap<ProductViewModel> {
    public ProductViewModelMap() {
        Map(p => p.Id);
        Map(p => p.Name);
        Map(m => m.Attributes).Name(new[] { "Attribute1", "Attribute2" });
    }
}

var products = new List<ProductViewModel> {
    new ProductViewModel { Id = 1, Name = "Product 1", Attributes = new Dictionary<string, object?> { { "Attribute1", 10 }, { "Attribute2", "Value" } } },
    new ProductViewModel { Id = 2, Name = "Product 2", Attributes = new Dictionary<string, object?> { { "Attribute1", 20 }, { "Attribute2", "Value" } } }
};

using (var writer = new StreamWriter("file.csv"))
using (var csv = new CsvWriter(writer, new CsvConfiguration(CultureInfo.InvariantCulture))) {
    csv.Context.RegisterClassMap<ProductViewModelMap>();
    await csv.WriteRecordsAsync(products);
}

However this produces:

Id,Name,Attribute1
1,Product 1,10,Value
2,Product 2,20,Value

But I would like:

Id,Name,Attribute1,Attribute2
1,Product 1,10,Value
2,Product 2,20,Value

I've tried numerous things but nothing seems to work. Ideally I'd like to use a ClassMap instead of writing the raw contents as I think this should be supported out of the box.

@nfplee nfplee added the feature label May 17, 2023
@nfplee nfplee closed this as completed May 19, 2023
@b-maslennikov
Copy link

why you've closed it? have you found the solution to get expected result Id,Name,Attribute1,Attribute2?
I have the same problem now.

@nfplee
Copy link
Author

nfplee commented Aug 6, 2024

Hi @b-maslennikov,

I'm not sure why I closed this out. For now I got around this by doing the following:

using (var writer = new StreamWriter("file.csv"))
using (var csv = new CsvWriter(writer, new CsvConfiguration(CultureInfo.InvariantCulture) {
    HasHeaderRecord = false
})) {
    csv.Context.RegisterClassMap<ProductViewModelMap>();
    csv.WriteHeader<ProductViewModel>(["Attribute1", "Attribute2"]);
    await csv.WriteRecordsAsync(products);
}```

Here's my extension method to manually write the header:

```cs
public static class CsvWriterExtensions {
    public static void WriteHeader<T>(this CsvWriter csv, IEnumerable<string> attributeNames) {
        foreach (var property in typeof(T).GetProperties().Where(p => p.Name != "Attributes")) {
            csv.WriteField(property.Name);
        }

        foreach (var attributeName in attributeNames) {
            csv.WriteField(attributeName);
        }

        csv.NextRecord();
    }
}

@nfplee nfplee reopened this Aug 6, 2024
@b-maslennikov
Copy link

My temporary solution: replace classmap with method that converts T to dynamic. + WriteDynamicHeader()
It would great to have requested functionality...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants