user3732793
user3732793

Reputation: 1979

CsvHelper just writing header for Class List

here and on the documentation of CsvHelper are numerous exaples of CSV writing with CsvHelper. None did work for me writing a list of classes

The List is of these Objects

class Contract
{
    public Contract()
    {
        Id = "None";
        Type = "";
        FileNames = new List<string>();
    }
    public string Id { get; set; }
    public string Type { get; set; }
    public List<String> FileNames { get; set; }
    public override string ToString()
    {
        string result = "Contract:ID " + Id
        + ",Type " + Type
        + ",FileNames ";
        foreach (string FileName in FileNames)
        {
            result = result + FileName + ",";
        }
        return result;
    }
}

This

    public void SaveCsv(string PathCsv)
    {
        Console.WriteLine("save Contracts to " + PathCsv);

        var config = new CsvConfiguration(CultureInfo.InvariantCulture)
        {
            Delimiter = ";",
            Encoding = Encoding.UTF8
        };
        using (var writer = new StreamWriter(PathCsv))
        using (var csv = new CsvWriter(writer, config))
        {
            csv.WriteHeader<Contract>();
            foreach (var contract in contracts)
            {
                csv.WriteRecord<Contract>(contract);
                csv.NextRecord();
            }
        }
    }

brings back only the headers without the contracts List. How to properly write the contracts list as csv ?

Upvotes: 0

Views: 1582

Answers (2)

Andrew Morton
Andrew Morton

Reputation: 25057

The problem as posed is, as noted by user nilsK in a comment, that the code is missing csv.NextRecord(); after writing the header.

In addition, it looks like you would want to write the filenames to the CSV file too. As CsvHelper won't directly write the List<string> for you, you can add another property to the Contract class which it can write.

It would be better to separate the filenames with a character which won't appear in a filename, for example a semi-colon.

using CsvHelper;
using CsvHelper.Configuration;
using CsvHelper.TypeConversion;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;

namespace CsvWrite
{

    public class Program
    {
        public class ContractMap : ClassMap<Contract>
        {
            public ContractMap()
            {
                Map(m => m.Id).Index(0).Name("Id");
                Map(m => m.Type).Index(1).Name("Type");
                Map(m => m.FileNames).Index(2).Name("Filenames");
            }
        }

        public class Contract
        {
            public string Id { get; set; }
            public string Type { get; set; }
            public List<String> FileNames { get; set; }
            public string Filenames { get => string.Join(";", FileNames); }

            public Contract()
            {
                Id = "None";
                Type = "";
                FileNames = new List<string>();
            }

            public override string ToString()
            {
                return "Contract:ID " + Id
                + ",Type " + Type
                + ",FileNames " + string.Join(',', FileNames);
            }
        }

        public static void SaveCsv(string PathCsv, List<Contract> contracts)
        {
            Console.WriteLine("Save Contracts to " + PathCsv);

            var config = new CsvConfiguration(CultureInfo.InvariantCulture)
            {
                Delimiter = ";",
                Encoding = Encoding.UTF8
            };
            using (var writer = new StreamWriter(PathCsv))
            using (var csv = new CsvWriter(writer, config))
            {
                csv.WriteRecords(contracts);
            }
        }

        static void Main(string[] args)
        {
            var contracts = new List<Contract>();

            contracts.Add(new Contract { Type = "A", Id = "1"});
            contracts.Add(new Contract { Type = "BB", Id = "100", FileNames = new List<string> { "F1", "F2" } });
            contracts.Add(new Contract { Type = "CCC", Id = "1111", FileNames = new List<string> { "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10" } });

            string f = @"C:\temp\SaveCsv.csv";
            SaveCsv(f, contracts);
        }
    }
}

Outputs:

Id;Type;Filenames
1;A;
100;BB;"F1;F2"
1111;CCC;"F1;F2;F3;F4;F5;F6;F7;F8;F9;F10"

Upvotes: 2

David Specht
David Specht

Reputation: 9094

If you know the number of filenames you could have it write out the header like this, using a ClassMap and .Index() with the start and end index.

Id;Type;FileName1;FileName2;FileName3

void Main()
{
    var records = new List<Contract>
    {
        new Contract { Id = "First", Type = "MyType", FileNames = new List<string> {"one","two","three"} },
        new Contract { Id = "Second", Type = "MyOtherType", FileNames = new List<string> {"four","five","six"} }
    };

    var config = new CsvConfiguration(CultureInfo.InvariantCulture)
    {
        Delimiter = ";",
        Encoding = Encoding.UTF8
    };

    //using (var writer = new StreamWriter("path\\to\\file.csv"))
    using (var csv = new CsvWriter(Console.Out, config))
    {
        csv.Context.RegisterClassMap<ContractMap>();
        
        csv.WriteRecords(records);
    }
}

class ContractMap : ClassMap<Contract>
{
    public ContractMap()
    {
        Map(x => x.Id);
        Map(x => x.Type);
        Map(x => x.FileNames).Name("FileName").Index(3,5);
    }
}

class Contract
{
    public Contract()
    {
        Id = "None";
        Type = "";
        FileNames = new List<string>();
    }
    public string Id { get; set; }
    public string Type { get; set; }
    public List<String> FileNames { get; set; }
    public override string ToString()
    {
        string result = "Contract:ID " + Id
        + ",Type " + Type
        + ",FileNames ";
        foreach (string FileName in FileNames)
        {
            result = result + FileName + ",";
        }
        return result;
    }
}

Upvotes: 0

Related Questions