Csvhelper writer datasourcs - c#

I am using csvhelper to import certain fields in a datagridview with no issues and then I have other columns added that has user imputed data. That part works.
Now when I try to write the datagridview to a new csv file and I get no data just headers that I have from the class. So where am I going wrong?
My write to code:
private void SchedSaveBtn_Click(object sender, EventArgs e)
{
var records = new List<WriteCsvschedule>
{
new WriteCsvschedule { EmpName = "1", EmpPayrollID = "2" },
};
using (var writer = new StreamWriter(#"c:\rcs\export\sched.csv"))
using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
{
csv.WriteRecords(records);
}
}
and my class:
public class WriteCsvschedule
{
[Index(0)]
public string EmpName { get; set; }
[Index(1)]
public string EmpPayrollID { get; set; }
[Index(2)]
public string JobName { get; set; }
}

Related

Why CsvHelper does not read data from CSV files

I'm trying to create a windows service that detects if there are new CSV files in a folder and parse those files. CsvHelper seems to not be able to read the CSV file. All the parameters that I try to populate are empty.
Is there something wrong in the code? The GetField method does not return any values and If I print the parameters are all empty.
The path is correct and the csv files paths are also correct.
public class CSVBatch
{
public string MaterialID { get; set; }
public string MaterialName { get; set; }
public string Location { get; set; }
public string Quantity { get; set; }
public string BatchID { get; set; }
public string ProcessOrder { get; set; }
public string Recipe { get; set; }
public List<CSVRawMaterial> CSVRawMaterials { get; set; }
public class CSVRawMaterial
{
public string MaterialID { get; set; }
public string MaterialName { get; set; }
public string Location { get; set; }
public string Quantity { get; set; }
public string BatchID { get; set; }
public string ProcessOrder { get; set; }
public string Recipe { get; set; }
}
}
protected override void OnStart(string[] args)
{
var folder = "C:\\BOM";
FileSystemWatcher fileSystemWatcher = new FileSystemWatcher(folder);
var fw = fileSystemWatcher;
fw.IncludeSubdirectories = true;
fw.EnableRaisingEvents = true;
fw.Created += Newfileevent;
}
static void Newfileevent(object sender, FileSystemEventArgs e)
{
string[] filePaths = Directory.GetFiles("C:\\BOM");
foreach (string s in filePaths)
{
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
Delimiter = ",",
MissingFieldFound = null,
TrimOptions = TrimOptions.Trim,
HeaderValidated = null,
HasHeaderRecord = true
};
using (var reader = new StringReader(s))
using (var csv = new CsvReader(reader, config))
{
csv.Read();
var batch = new CSVBatch
{
MaterialID = csv.GetField<string>(0),
MaterialName = csv.GetField<string>(1),
Location = csv.GetField<string>(2),
Quantity = csv.GetField<string>(3),
BatchID = csv.GetField<string>(4),
ProcessOrder = csv.GetField<string>(5),
Recipe = csv.GetField<string>(6)
};
csv.Read();
var rawMaterials = new List<CSVRawMaterial>();
while (csv.Read())
{
var rawmaterial = new CSVRawMaterial
{
MaterialID = csv.GetField<string>(0),
MaterialName = csv.GetField<string>(1),
Location = csv.GetField<string>(2),
Quantity = csv.GetField<string>(3)
};
rawMaterials.Add(rawmaterial);
}
batch.CSVRawMaterials = rawMaterials;
}
}
CSV File:
You have 2 issues.
You are using StringReader instead of StreamReader. It should be:
using (var reader = new StreamReader(s))
If you have a header row, you also have to specifically read the header when reading by hand.
using (var reader = new StreamReader(s))
using (var csv = new CsvReader(reader, config))
{
csv.Read();
csv.ReadHeader();
csv.Read();
batch = new CSVBatch
{

csvHelper add new column beside last one

Edit, thank you for the suggestion of using csvhelper, this is actually helping quite a lot.
What I have done is create a new method like so:
public static void AppendFile<T>(FileInfo fi, List<T> report)
{
var settings = new CsvConfiguration(new CultureInfo("en-GB"))
{
//Delimiter = ";"
};
using var stream = File.Open(fi.FullName, FileMode.Append);
using var writer = new StreamWriter(stream);
using (var csv = new CsvWriter(writer, settings))
{
csv.WriteRecords(report);
}
}
And gone through the example on the csvhelper site, creating a new class:
public class Foo
{
public int Id { get; set; }
public string Name { get; set; }
}
and then creating a new list:
public class Foo
{
public int Id { get; set; }
public string Name { get; set; }
}
and calling it like so:
AppendToFile.AppendFile(exportFile, records1);
This is working better than what I had before but, instead adding the new columns beside the last column, they are getting added at the bottom of the file.
For clarification,
what I'm trying to do:
what I'm getting:
As you'll be able to see, it's just being added as new rows rather than being separate columns, what do I need to change?
Another way you can accomplish your goal is to write each object to the row by hand using csvWriter.WriteHeader and csvWriter.WriteRecord.
void Main()
{
var settings = new CsvConfiguration(new CultureInfo("en-GB"))
{
Delimiter = ";"
};
var fooList = new List<Foo>()
{
new Foo { Id = 67, Name = "test1,test2"}
};
List<MyClass> records;
using (var reader = new StringReader("Author,Admin Owner\nChris Jones,\nJohn Thompson,\nBrian Oates,"))
using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
{
records = csv.GetRecords<MyClass>().ToList();
}
using var csvWriter = new CsvWriter(Console.Out, settings);
csvWriter.WriteHeader<MyClass>();
csvWriter.WriteHeader<Foo>();
csvWriter.NextRecord();
var i = 0;
foreach (var record in records)
{
csvWriter.WriteRecord(record);
if (i < fooList.Count())
{
csvWriter.WriteRecord(fooList[i]);
}
csvWriter.NextRecord();
i++;
}
}
public class MyClass
{
public string Author { get; set; }
[Name("Admin Owner")]
public string AdminOwner { get; set; }
}
public class Foo
{
public int Id { get; set; }
public string Name { get; set; }
}
I managed to figure out a way that worked for me, might not be the most efficient but it does work.
public static void AppendFile(FileInfo fi, List<string> newColumns, DataTable newRows)
{
var settings = new CsvConfiguration(new CultureInfo("en-GB"))
{
Delimiter = ";"
};
var dt = new DataTable();
using (var reader = new StreamReader(fi.FullName))
using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
{
using (var dataReader = new CsvDataReader(csv))
{
dt.Load(dataReader);
foreach (var title in newColumns)
{
dt.Columns.Add(title);
}
dt.Rows.Clear();
foreach (DataRow row in newRows.Rows)
{
dt.Rows.Add(row.ItemArray);
}
}
}
using var streamWriter = new StreamWriter(fi.FullName);
using var csvWriter = new CsvWriter(streamWriter, settings);
// Write columns
foreach (DataColumn column in dt.Columns)
{
csvWriter.WriteField(column.ColumnName);
}
csvWriter.NextRecord();
// Write row values
foreach (DataRow row in dt.Rows)
{
for (var i = 0; i < dt.Columns.Count; i++)
{
csvWriter.WriteField(row[i]);
}
csvWriter.NextRecord();
}
}
I start by getting the contents of the csv file into a data table and then adding in the new columns that I need. I then clear all the rows in the datatable and add new ones in (the data that is removed is added back in via the newRows parameter) and then write the datatable to the csv file

Read CSV files without Header using CSVHelper

I have a lot of CSV files without header and need to read it in C#. I manually added header to one of these files and with the following code using CSVHelper I can read the files and show them in a GridView.
Now my question is, how can I read these files without a header? Or how can I add a header (a new record) using CSVHelper in the first line?
public Form1()
{
InitializeComponent();
List<Festival> records;
var config = new CsvConfiguration(CultureInfo.InvariantCulture) { Delimiter = ";" };
using (var reader = new StreamReader(#"File8.csv"))
using(var csv = new CsvReader(reader, config))
{
records = csv.GetRecords<Festival>().ToList();
}
dataGridView1.DataSource = records;
}
Class
public class Festival
{
public string Day { get; set; }
public string Start { get; set; }
public int Lenght { get; set; }
public string FilmName { get; set; }
public float Rating { get; set; }
}
csv sample
Mi;22:15;110;A;8
Mi;19:00;106;B;8
Mi;19:15;97;C;8.2
Add column-index mapping attributes to the target members:
public class Festival
{
[Index(0)]
public string Day { get; set; }
[Index(1)]
public string Start { get; set; }
[Index(2)]
public int Lenght { get; set; }
[Index(3)]
public string FilmName { get; set; }
[Index(4)]
public float Rating { get; set; }
}
And specify HasHeaderRecord = false in the config:
var config = new CsvConfiguration(CultureInfo.InvariantCulture) { Delimiter = ";", HasHeaderRecord = false };
If modifying the target model isn't desirable, implement a ClassMap instead:
public sealed class FestivalMap : ClassMap<Festival>
{
public FestivalMap()
{
Map(f => f.Day).Index(0);
Map(f => f.Start).Index(1);
Map(f => f.Lenght).Index(2);
Map(f => f.FilmName).Index(3);
Map(f => f.Rating).Index(4);
}
}
And register it like this before fetching the records (you still need to specify HasHeaderRecord = false in the config):
csv.Context.RegisterClassMap<FestivalMap>();
records = csv.GetRecords<Festival>().ToList();

Csvhelper Ingore is removing only header column name but no the actual data

I loaded a csv file in my database with a DbId column not in the file.
I want to export it back to the original format.
My csvhelper mapping is in MyCsvClass with Map(m => m.DbId).Ignore();
Header is fine, but output data is still showing values of DbId column:
https://dotnetfiddle.net/XP2Vvq
using CsvHelper.Configuration;
using System;
using System.IO;
namespace Test
{
class Program
{
static void Main(string[] args)
{
var record = new { DbId = 1, Data1 = "aaa", Data2 = "bbb" };
using (var sw = new StreamWriter(#"c:/temp/testt.csv"))
{
using (var csvWriter = new CsvHelper.CsvWriter(sw))
{
csvWriter.Configuration.RegisterClassMap<MyCsvClassMap>();
csvWriter.WriteHeader<MyCsvClass>();
csvWriter.NextRecord();
csvWriter.WriteRecord(record);
}
}
}
}
public class MyCsvClassMap : ClassMap<MyCsvClass>
{
public MyCsvClassMap()
{
AutoMap();
Map(m => m.DbId).Ignore();
}
}
public class MyCsvClass
{
public int DbId { get; set; }
public string Data1 { get; set; }
public string Data2 { get; set; }
}
}
Output is
Data1, Data2
1, "aaa", "bbb"
when I expect
Data1, Data2
"aaa", "bbb"
The issue with your code is that you create an instance of anonymous type
var record = new { DbId = 1, Data1 = "aaa", Data2 = "bbb" };
instead of
var record = new MyCsvClass { DbId = 1, Data1 = "aaa", Data2 = "bbb" };
The header is fine, because you pass the correct class to type parameter of the generic method.
csvWriter.WriteHeader<MyCsvClass>();
Edit
To export DB entities to CSV you don't need any intermediate class. You can write entities directly to CSV and ClassMap<T> helps you control what values and how get serialized to CSV. If your entity class is MyDbEntity, then just register custom mapping ClassMap<MyDbEntity> where you auto-map all fields ignoring some fields as you did in your MyCsvClassMap.
I think the ClassMap is not correctly configured. No idea why your example is working because it should not compile.
Can you modify the following line of code to register the ClassMap (MyCsvClassMap instead of MyCsvClass):
csvWriter.Configuration.RegisterClassMap<MyCsvClassMap>();
The rest of your example works fine.
Try the following console app:
class Program
{
static void Main(string[] args)
{
var allRecords = new List<MyCsvClass>()
{
new MyCsvClass { DbId = "1", Data1 = "data1", Data2 = "data2"}
};
using (var sw = new StreamWriter("C:\\temp\\test.csv"))
{
using (var csvWriter = new CsvHelper.CsvWriter(sw))
{
csvWriter.Configuration.RegisterClassMap<MyCsvClassMap>();
csvWriter.WriteHeader<MyCsvClass>();
csvWriter.NextRecord();
csvWriter.WriteRecords(allRecords);
}
}
}
public class MyCsvClassMap : ClassMap<MyCsvClass>
{
public MyCsvClassMap()
{
AutoMap();
Map(m => m.DbId).Ignore();
}
}
public class MyCsvClass
{
public string DbId { get; set; }
public string Data1 { get; set; }
public string Data2 { get; set; }
}
}
If this is working there is maybe some other code which causes this behavior.

How to retrieve only few columns data of a csv using the column names instead of column number in c#

I have a csv consisting of many columns. From that csv I have to select only few required columns.
The code I have written is
for (int i = 0; i < lineCount; i++)
{
var line = str.ReadLine();
if (line != null)
{
var values = line.Split(',');
dataInformation.Add(new DataInformation
{
timestamp_iso = values[3],
last_attributed_touch_data_tilde_campaign = values[9],
last_attributed_touch_data_tilde_channel = values[11],
last_attributed_touch_data_tilde_feature = values[12],
last_attributed_touch_data_tilde_ad_set_name = values[19],
user_data_platform = values[69],
user_data_aaid = values[70],
user_data_idfa = values[71],
user_data_idfv = values[72]
});
}
}
I am getting wrong values while using this. Is there any other approach to retrieve the values using the column names instead of column numbers.
The Data Information is a class
public class DataInformation
{
public string timestamp_iso { get; set; }
public string last_attributed_touch_data_tilde_campaign { get; set; }
public string last_attributed_touch_data_tilde_channel { get; set; }
public string last_attributed_touch_data_tilde_feature { get; set; }
public string last_attributed_touch_data_tilde_ad_set_name { get; set; }
public string user_data_platform { get; set; }
public string user_data_aaid { get; set; }
public string user_data_idfa { get; set; }
public string user_data_idfv { get; set; }
}
Please help me on this.
I recommend using a library to deal with CSV format. CsvHelper is a good one. It allows accessing fields by column name:
csv.Read();
var field = csv["HeaderName"];
CSV format may look simple, but there are a few corner cases (like quotes), so it is better to use an existing solution.
I have used the below code to get all the records of the type DataInformation.
using (TextReader fileReader = File.OpenText(FileName))
{
var csv = new CsvReader(fileReader);
dataInformation = csv.GetRecords<DataInformation>().ToList();
}
And after that I have used the below code to get the required columns.
using (TextWriter writer = new StreamWriter(ConfigurationManager.AppSettings["downloadFilePath"] + ConfigurationManager.AppSettings["fileName"] + date + ConfigurationManager.AppSettings["csvExtension"].ToString()))
{
using (var csv = new CsvWriter(TextWriter.Synchronized(writer)))
{
csv.WriteHeader(typeof(DataInformation));
csv.NextRecord();
csv.WriteRecords(dataInformation);
}
}
It works for me.

Categories

Resources