My application reads .CSV file(which do not having a header in csv file) and converts into XML file.
For existing code wrote as
sr = new StreamReader(fs);
fs = null;
using (CsvReader csvReader = new CsvReader(sr))
{
sr = null;
csvReader.Configuration.HasHeaderRecord = hasHeaderRecord;
csvReader.Configuration.IgnoreBlankLines = false;
csvReader.Configuration.IgnoreReadingExceptions = true;
csvReader.Configuration.WillThrowOnMissingField = false;
csvReader.Configuration.TrimFields = true;
csvReader.Configuration.RegisterClassMap<Class1Map>();
FileRecords = csvReader.GetRecords<Class1>().ToList();
}
public class Class1Map : CsvClassMap<Class1>
{
public Class1Map()
{
Map(m => m.AccountId).Index(0);
Map(m => m.MeterId).Index(1);
.......
.......
}
}
But now for my new requirement, .csv file includes header and column names that are different compared to previous .csv. Somehow I have read the new CSV file and get values present in the csv file and mapped to class1.
Class1 properties are AccountId,MeterId etc.
But in new format the names are different now.
AccountId as AccountRef and MeterId as MeterSerial.
Can any one suggest how to map new file values of AccountRef,MeterSerial to class1 properties AccountId,MeterId
You could just add .Name() to your maps. Your first example with no header will use .Index() and your second example with a header will use .Name() to map the columns.
void Main()
{
var config1 = new CsvConfiguration(CultureInfo.InvariantCulture)
{
HasHeaderRecord = false
};
using (var reader = new StringReader("1,2\n3,4"))
using (var csv = new CsvReader(reader, config1))
{
csv.Context.RegisterClassMap<Class1Map>();
var records = csv.GetRecords<Class1>().Dump();
}
var config2 = new CsvConfiguration(CultureInfo.InvariantCulture)
{
HasHeaderRecord = true
};
using (var reader = new StringReader("MeterSerial,AccountRef\n4,5\n6,7"))
using (var csv = new CsvReader(reader, config2))
{
csv.Context.RegisterClassMap<Class1Map>();
var records = csv.GetRecords<Class1>().Dump();
}
}
public class Class1Map : ClassMap<Class1>
{
public Class1Map()
{
Map(m => m.AccountId).Index(0).Name("AccountRef");
Map(m => m.MeterId).Index(1).Name("MeterSerial");
}
}
public class Class1
{
public int AccountId { get; set; }
public int MeterId { get; set; }
}
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!
Possibly the easiest way is going to be to manually write out the dictionary part.
*** Update to work with CsvHelper Version 2.8.4 ***
void Main()
{
var records = new List<classA>
{
new classA {
Amount = 15,
Dict = new Dictionary<string,string>{["a1"] = "a2",["b1"] = "b2"}
}
};
using (var csv = new CsvWriter(Console.Out))
{
var dict = records.First().Dict;
var properties = typeof(classA).GetProperties();
foreach (PropertyInfo property in properties)
{
if (property.Name != "Dict")
{
csv.WriteField(property.Name);
}
}
foreach (var item in dict)
{
csv.WriteField(item.Key);
}
csv.NextRecord();
foreach (var record in records)
{
foreach (PropertyInfo property in properties)
{
if (property.Name != "Dict")
{
csv.WriteField(property.GetValue(record));
}
}
foreach (var item in record.Dict)
{
csv.WriteField(item.Value);
}
csv.NextRecord();
}
}
}
// You can define other methods, fields, classes and namespaces here
public class classA
{
public int Amount { get; set; }
public Dictionary<string, string> Dict { get; set; }
}
*** Works for current Version 27.2.1 ***
void Main()
{
var records = new List<classA>
{
new classA { Amount = 15, Dict = new Dictionary<string,string>{["a1"] = "a2",["b1"] = "b2"} },
};
using (var csv = new CsvWriter(Console.Out, CultureInfo.InvariantCulture))
{
var dict = records.First().Dict;
csv.WriteHeader<classA>();
foreach (var item in dict)
{
csv.WriteField(item.Key);
}
csv.NextRecord();
foreach (var record in records)
{
csv.WriteRecord(record);
foreach (var item in record.Dict)
{
csv.WriteField(item.Value);
}
csv.NextRecord();
}
}
}
public class classA
{
public int Amount { get; set; }
public Dictionary<string, string> Dict { get; set; }
}
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
I have this code to read a simple CSV file with the CsvHelper library
public class CSVData
{
public string Time { get; set; }
public double Value1 { get; set; }
public double Value2 { get; set; }
public double Value3 { get; set; }
}
using (var reader = new StreamReader("c:\\temp\\test.csv"))
using (var csv = new CsvReader(reader))
{
var records = csv.GetRecords<CSVData>();
}
and this is how th CSV data looks
"Time","Value1","Value2","Value3"
8/28/2019 4:32:09 PM,2.03,10229.73,10437.51821998
and I get this error when I try to read the records with
foreach (CSVData item in records)
CsvHelper.BadDataException: 'You can ignore bad data by setting BadDataFound to null.'
Your code actually worked fine for me as long as you use the US culture setting for double (using a . decimal point).
class Program
{
static void Main(string[] args)
{
Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US", false);
List<CSVData> records;
using (var reader = new StreamReader("test.csv"))
using (var csv = new CsvReader(reader))
{
records = csv.GetRecords<CSVData>().ToList();
}
foreach (var r in records)
{
Console.WriteLine($"Time:{r.Time} Value1:{r.Value1} Value2:{r.Value2} Value3:{r.Value3}");
}
}
}
This outputs:
Time: 8/28/2019 4:32:09 PM Value1:2.03 Value2:10229.73 Value3:10437.51821998
Try setting the culture info in the configuration. You may also need to set the delimiter.
using (var reader = new StreamReader("c:\\temp\\test.csv"))
using (var csv = new CsvReader(reader))
{
csv.Configuration.CultureInfo = new CultureInfo("en-US", false);
csv.Configuration.Delimiter = ",";
var records = csv.GetRecords<CSVData>();
}
You can try to set CultureInfo to CurrentCulture.
csv.Configuration.CultureInfo = CultureInfo.CurrentCulture;
I have the following object structure and trying to write to csv using csvhelper. but the filenames column in not getting added.
public class ClusterData
{
public IEnumerable<string> FileName { get; set; }
public int? ClusterNumber { get; set; }
public string TopTerm { get; set; }
}
using (var writer = new StreamWriter(#"C:\Clean.csv"))
{
var csv = new CsvWriter(writer);
csv.WriteHeader<ClusterData>();
foreach (var item in dataToCsv)
{
foreach (var filename in item.FileName)
{
csv.WriteField(filename);
csv.WriteField(item.ClusterNumber);
csv.WriteField(item.TopTerm);
csv.NextRecord();
}
}
writer.Flush();
}
how to achieve with this?i want the outer loop to be repeated once and inner loop to be repeated for each item in filename.
Thanks
Extract the desired data and then use the writer to send it to file
using (var writer = new StreamWriter(#"C:\Clean.csv")) {
var data = new List<ClusterData>();
//...assuming data is poulated
var dataToCsv = data.SelectMany(item => item.FileName.Select(filename => new {
FileName = filename,
ClusterNumber = item.ClusterNumber,
TopTerm = item.TopTerm
}));
var csv = new CsvWriter(writer);
csv.WriteRecords(dataToCsv);
}
A linq query is used to construct the desired object format for each file name in the data.
The data is then converted to CSV as it normally would using a CsvWriter
I've been stuck trying to get the CSV Helper to write to a file. When I run DownloadRegistrantsCsv it downloads the file with the proper name and everything else, but it never writes anything to it.
public async Task<Stream> GetDownloadStreamAsync(int id)
{
var memoryStream = new MemoryStream();
var streamWriter = new StreamWriter(memoryStream);
var streamReader = new StreamReader(memoryStream);
var csvHelper = new CsvHelper.CsvWriter(streamWriter);
csvHelper.WriteRecord(new EventRegistrant { FirstName = "Max" });
await memoryStream.FlushAsync();
memoryStream.Position = 0;
return memoryStream;
}
public async Task<ActionResult> DownloadRegistrantsCsv(int id)
{
var #event = await _service.GetAsync(id, true);
if (#event == null)
return HttpNotFound();
var stream = await _service.GetDownloadStreamAsync(id);
return File(stream, "application/txt", "test" + ".csv");
}
I've also tried just using the documentation for the CSV Helper and I can't even get that to write. Here's what I've got for that...
// Copyright 2009-2015 Josh Close and Contributors
// This file is a part of CsvHelper and is dual licensed under MS-PL and Apache 2.0.
// See LICENSE.txt for details or visit http://www.opensource.org/licenses/ms-pl.html for MS-PL and http://opensource.org/licenses/Apache-2.0 for Apache 2.0.
// http://csvhelper.com
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.Web.Script.Serialization;
using CsvHelper.Configuration;
using CsvHelper.TypeConversion;
namespace CsvHelper.Example
{
class Program
{
private const string columnSeparator = ":";
static void Main(string[] args)
{
//ReadRawFieldsByIndex();
//ReadRawFieldsByName();
//ReadFieldsByIndex();
//ReadRecordsNoAttributes();
//ReadRecordsWithAttributes();
//ReadAllRecords();
//WriteRawFields();
//WriteFields();
WriteRecordsNoAttributes();
//WriteRecordsWithAttributes();
WriteAllRecords();
Console.ReadKey();
}
public static void ReadRawFieldsByIndex()
{
Console.WriteLine("Raw fields by index:");
using (var reader = new CsvReader(new StreamReader(GetDataStream(true, true))))
{
while (reader.Read())
{
Console.Write(reader.GetField(0) + columnSeparator);
Console.Write(reader.GetField(1) + columnSeparator);
Console.Write(reader.GetField(2) + columnSeparator);
Console.WriteLine(reader.GetField(3));
}
}
Console.WriteLine();
}
public static void ReadRawFieldsByName()
{
Console.WriteLine("Raw fields by name:");
using (var reader = new CsvReader(new StreamReader(GetDataStream(true, true))))
{
while (reader.Read())
{
Console.Write(reader.GetField("String Column") + columnSeparator);
Console.Write(reader.GetField("Int Column") + columnSeparator);
Console.Write(reader.GetField("Guid Column") + columnSeparator);
Console.Write(reader.GetField("Does Not Exist Column") + columnSeparator);
Console.WriteLine(reader.GetField("Custom Type Column"));
}
}
Console.WriteLine();
}
public static void ReadFieldsByIndex()
{
Console.WriteLine("Fields by index:");
var customTypeTypeConverter = new CustomTypeTypeConverter();
using (var reader = new CsvReader(new StreamReader(GetDataStream(true, true))))
{
while (reader.Read())
{
Console.Write(reader.GetField<string>(0) + columnSeparator);
Console.Write(reader.GetField<int>("Int Column") + columnSeparator);
Console.Write(reader.GetField<Guid>(2) + columnSeparator);
Console.WriteLine(reader.GetField<CustomType>(3, customTypeTypeConverter));
}
}
Console.WriteLine();
}
public static void ReadRecordsNoAttributes()
{
Console.WriteLine("Records no attributes:");
using (var reader = new CsvReader(new StreamReader(GetDataStream(true, false))))
{
while (reader.Read())
{
Console.WriteLine(reader.GetRecord<CustomObject>());
}
}
Console.WriteLine();
}
public static void ReadRecordsWithAttributes()
{
Console.WriteLine("Records with attributes:");
using (var reader = new CsvReader(new StreamReader(GetDataStream(true, true))))
{
reader.Configuration.RegisterClassMap<CustomObjectWithMappingMap>();
while (reader.Read())
{
Console.WriteLine(reader.GetRecord<CustomObjectWithMapping>());
}
}
Console.WriteLine();
}
public static void ReadAllRecords()
{
Console.WriteLine("All records:");
using (var reader = new CsvReader(new StreamReader(GetDataStream(true, false))))
{
var records = reader.GetRecords<CustomObject>();
foreach (var record in records)
{
Console.WriteLine(record);
}
}
Console.WriteLine();
}
public static void WriteRawFields()
{
Console.WriteLine("Write raw fields");
using (var memoryStream = new MemoryStream())
using (var streamWriter = new StreamWriter(memoryStream))
using (var streamReader = new StreamReader(memoryStream))
using (var writer = new CsvWriter(streamWriter))
{
writer.WriteField("String Column");
writer.WriteField("Int Column");
writer.WriteField("Guid Column");
writer.WriteField("Custom Type Column");
writer.NextRecord();
writer.WriteField("one");
writer.WriteField((1).ToString());
writer.WriteField(Guid.NewGuid().ToString());
writer.WriteField((new CustomType { First = 1, Second = 2, Third = 3 }).ToString());
writer.NextRecord();
memoryStream.Position = 0;
Console.WriteLine(streamReader.ReadToEnd());
}
Console.WriteLine();
}
public static void WriteFields()
{
Console.WriteLine("Write fields");
using (var memoryStream = new MemoryStream())
using (var streamWriter = new StreamWriter(memoryStream))
using (var streamReader = new StreamReader(memoryStream))
using (var writer = new CsvWriter(streamWriter))
{
writer.WriteField("String Column");
writer.WriteField("Int Column");
writer.WriteField("Guid Column");
writer.WriteField("Custom Type Column");
writer.NextRecord();
writer.WriteField("one");
writer.WriteField(1);
writer.WriteField(Guid.NewGuid());
writer.WriteField(new CustomType { First = 1, Second = 2, Third = 3 });
writer.NextRecord();
memoryStream.Position = 0;
Console.WriteLine(streamReader.ReadToEnd());
}
Console.WriteLine();
}
public static void WriteRecordsNoAttributes()
{
Console.WriteLine("Write records no attributes:");
var records = new List<CustomObject>
{
new CustomObject
{
CustomTypeColumn = new CustomType
{
First = 1,
Second = 2,
Third = 3,
},
GuidColumn = Guid.NewGuid(),
IntColumn = 1,
StringColumn = "one",
},
new CustomObject
{
CustomTypeColumn = new CustomType
{
First = 4,
Second = 5,
Third = 6,
},
GuidColumn = Guid.NewGuid(),
IntColumn = 2,
StringColumn = "two",
},
};
using (var memoryStream = new MemoryStream())
using (var streamWriter = new StreamWriter(memoryStream))
using (var streamReader = new StreamReader(memoryStream))
using (var writer = new CsvWriter(streamWriter))
{
foreach (var record in records)
{
writer.WriteRecord(record);
}
memoryStream.Position = 0;
Console.WriteLine(streamReader.ReadToEnd());
}
Console.WriteLine();
}
public static void WriteRecordsWithAttributes()
{
Console.WriteLine("Write records with attributes:");
var records = new List<CustomObjectWithMapping>
{
new CustomObjectWithMapping
{
CustomTypeColumn = new CustomType
{
First = 1,
Second = 2,
Third = 3,
},
GuidColumn = Guid.NewGuid(),
IntColumn = 1,
StringColumn = "one",
},
new CustomObjectWithMapping
{
CustomTypeColumn = new CustomType
{
First = 4,
Second = 5,
Third = 6,
},
GuidColumn = Guid.NewGuid(),
IntColumn = 2,
StringColumn = "two",
},
};
using (var memoryStream = new MemoryStream())
using (var streamWriter = new StreamWriter(memoryStream))
using (var streamReader = new StreamReader(memoryStream))
using (var writer = new CsvWriter(streamWriter))
{
foreach (var record in records)
{
writer.WriteRecord(record);
}
memoryStream.Position = 0;
Console.WriteLine(streamReader.ReadToEnd());
}
Console.WriteLine();
}
public static void WriteAllRecords()
{
Console.WriteLine("Write all records with attributes:");
var records = new List<CustomObjectWithMapping>
{
new CustomObjectWithMapping
{
CustomTypeColumn = new CustomType
{
First = 1,
Second = 2,
Third = 3,
},
GuidColumn = Guid.NewGuid(),
IntColumn = 1,
StringColumn = "one",
},
new CustomObjectWithMapping
{
CustomTypeColumn = new CustomType
{
First = 4,
Second = 5,
Third = 6,
},
GuidColumn = Guid.NewGuid(),
IntColumn = 2,
StringColumn = "two",
},
};
using (var memoryStream = new MemoryStream())
using (var streamWriter = new StreamWriter(memoryStream))
using (var streamReader = new StreamReader(memoryStream))
using (var writer = new CsvWriter(streamWriter))
{
writer.Configuration.RegisterClassMap<CustomObjectWithMappingMap>();
writer.WriteRecords(records as IEnumerable);
memoryStream.Position = 0;
Console.WriteLine(streamReader.ReadToEnd());
}
Console.WriteLine();
}
public static MemoryStream GetDataStream(bool hasHeader, bool hasSpacesInHeaderNames)
{
var stream = new MemoryStream();
var writer = new StreamWriter(stream);
if (hasHeader)
{
var header = hasSpacesInHeaderNames
? "String Column,Int Column,Guid Column,Custom Type Column"
: "StringColumn,IntColumn,GuidColumn,CustomTypeColumn";
writer.WriteLine(header);
}
writer.WriteLine("one,1,{0},1|2|3", Guid.NewGuid());
writer.WriteLine("two,2,{0},4|5|6", Guid.NewGuid());
writer.WriteLine("\"this, has a comma\",2,{0},7|8|9", Guid.NewGuid());
writer.WriteLine("\"this has \"\"'s\",4,{0},10|11|12", Guid.NewGuid());
writer.Flush();
stream.Position = 0;
return stream;
}
public class CustomType
{
public int First { get; set; }
public int Second { get; set; }
public int Third { get; set; }
public override string ToString()
{
var serializer = new JavaScriptSerializer();
return serializer.Serialize(this);
}
}
public class CustomTypeTypeConverter : ITypeConverter
{
public string ConvertToString(TypeConverterOptions options, object value)
{
var obj = (CustomType)value;
return string.Format("{0}|{1}|{2}", obj.First, obj.Second, obj.Third);
}
public object ConvertFromString(TypeConverterOptions options, string text)
{
var values = ((string)text).Split('|');
var obj = new CustomType
{
First = int.Parse(values[0]),
Second = int.Parse(values[1]),
Third = int.Parse(values[2]),
};
return obj;
}
public bool CanConvertFrom(Type type)
{
throw new NotImplementedException();
}
public bool CanConvertTo(Type type)
{
throw new NotImplementedException();
}
}
public class CustomObject
{
public CustomType CustomTypeColumn { get; set; }
public Guid GuidColumn { get; set; }
public int IntColumn { get; set; }
public string StringColumn { get; set; }
public override string ToString()
{
var serializer = new JavaScriptSerializer();
return serializer.Serialize(this);
}
}
public class CustomObjectWithMapping
{
public CustomType CustomTypeColumn { get; set; }
public Guid GuidColumn { get; set; }
public int IntColumn { get; set; }
public string StringColumn { get; set; }
public string IgnoredColumn { get; set; }
//public override string ToString()
//{
// var serializer = new JavaScriptSerializer();
// return serializer.Serialize(this);
//}
}
public sealed class CustomObjectWithMappingMap : CsvClassMap<CustomObjectWithMapping>
{
public CustomObjectWithMappingMap()
{
Map(m => m.CustomTypeColumn).Name("Custom Type Column").Index(3).TypeConverter<CustomTypeTypeConverter>();
Map(m => m.GuidColumn).Name("Guid Column").Index(2);
Map(m => m.IntColumn).Name("Int Column").Index(1);
Map(m => m.StringColumn).Name("String Column").Index(0);
}
}
}
}
Can anyone point me to what I might be missing or doing wrong?
If you have a DataTable you can convert it to a Comma Separated Value list of strings like this...
/// <summary>
/// Creates a comma separated value string from a datatable.
/// </summary>
public static string ToCSV(DataTable table)
{
StringBuilder csv = new StringBuilder();
for(int i = 0; i < table.Columns.Count ;i++) // process the column headers
{
if (i > 0)
csv.Append(",");
csv.Append(_FormatToCSVField(table.Columns[i].ColumnName));
}
if (table.Columns.Count > 0)
csv.Append("\r\n");
for (int i = 0; i < table.Rows.Count; i++) // process the row data
{
for (int j = 0; j < table.Columns.Count; j++) // process each field in the data row.
{
if (j > 0)
csv.Append(",");
csv.Append(_FormatToCSVField(table.Rows[i][j].ToString()));
}
csv.Append("\r\n");
}
return csv.ToString();
}
private static string _FormatToCSVField(string unformattedField)
{
return "\"" + unformattedField.Replace("\"", "\"\"") + "\"";
}
Or if you didn't have a DataTable; take take your created comma separated value (CSV) list of string "row1 column 1, row1 column2, row1 column3, \r\n, row2, colum1... etc..."
and save it to a CSV File, like this...
//Your CSV String
string WhatToWrite = "row1 column 1, row1 column2, row1 column3, \r\n";
//Convert your CSV String to byte[]
byte[] PutWhatToWriteIntoBytes = Encoding.GetEncoding("iso-8859-1").GetBytes(WhatToWrite);
//Write the byte[] to CSV readable by excel
string filename = "WhatYouWantToCallYourFile" + ".csv";
Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
Response.AddHeader("content-disposition", filename.ToString());
Response.Clear();
Response.BinaryWrite(PutWhatToWriteIntoBytes);
Response.End();
Its really hard to follow all your code. What exactly is it that you are trying to write to a CSV...Get that; check if it is good. then write to the file. determine if you are writing an empty string, or if the writing is losing the string
Flushing the stream writer worked for me if you still want to use the CSV Helper