combine csv headers into new one - c#

I'm using CSVHelper library for parsing CSV files, but I have to map tow columns to a single one that contains the data for each one: like {name}: {firstName} {lastName}
Is there any way to do that?
Thanks

Does this do what you were looking for?
public class Program
{
public static void Main(string[] args)
{
using (MemoryStream stream = new MemoryStream())
using (StreamWriter writer = new StreamWriter(stream))
using (StreamReader reader = new StreamReader(stream))
using (CsvReader csv = new CsvReader(reader, CultureInfo.InvariantCulture))
{
writer.WriteLine("Id,FirstName,LastName");
writer.WriteLine("1,Bob,Barker");
writer.WriteLine("2,Davey,Jones");
writer.Flush();
stream.Position = 0;
csv.Configuration.RegisterClassMap<FooClassMap>();
var records = csv.GetRecords<Foo>().ToList();
}
}
}
public class FooClassMap : ClassMap<Foo>
{
public FooClassMap()
{
Map(m => m.Id);
Map(m => m.Name).ConvertUsing(row => row.GetField("FirstName") + " " + row.GetField("LastName"));
}
}
public class Foo
{
public int Id { get; set; }
public string Name { get; set; }
}

Related

How to map one class properties to another using class with different names using CsvClassMap

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; }
}

Write json string into one .csv column

I want to write a string into one Column in an .csv (Excel) file. My Problem is that the string is written into multiple Columns.
In this screenshot for example I have 20 Columns.
GetMetadataCompleteResponse resultValue = null;
string jsonData = null;
await Task.Run(() =>
{
byte[] rawData = Convert.FromBase64String(responseContent);
jsonData = CompressUtil.Unzip(rawData);
});
resultValue = JsonConvert.DeserializeObject<GetMetadataCompleteResponse>(jsonData);
foreach(string a in resultValue.Value.Values)
{
foreal += a;
}
await Log.Info("callWebservice for " + strUrl + ", Result: " + objErrorDetails.Code + ", " + foreal);
edit
I've noticed that the new Column starts after every ';'(semicolon). I probably can just replace it with something else.
I think you have 2 issues. The first one is how you write your CSV with simple string concatenation. With no escaping or double quote.
The Json will have commas , that will be separator in your CSV.
In order to produc e a valid CSV you should read the RFC 4180 and use a proper library to handle the Serialisation.
Here is an Minimal, Complete, and Verifiable example of writing a Json in a CSV column.
using CsvHelper;
using CsvHelper.Configuration;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
public class Program
{
public static void Main()
{
var input = new Foo
{
Label = "My Foo",
Bars = new List<Bar> {
new Bar{Label="Bar2"},
new Bar{Label="Bar1"},
new Bar{Label="Bar3"},
}
};
var json = JsonConvert.SerializeObject(input);
var myObject = new CsvObject
{
Label = "My CSV object",
FooString = json,
};
var result = "";
// Writing into a string instead of a file for debug purpuse.
using (var stream = new MemoryStream())
using (var reader = new StreamReader(stream))
using (var writer = new StreamWriter(stream))
using (var csv = new CsvWriter(writer))
{
csv.Configuration.RegisterClassMap<CsvObjectMap>();
csv.WriteHeader<CsvObject>();
csv.NextRecord();
csv.WriteRecord(myObject);
csv.NextRecord();
writer.Flush();
stream.Position = 0;
result = reader.ReadToEnd();
}
Console.WriteLine(result);
}
private sealed class CsvObjectMap : ClassMap<CsvObject>
{
public CsvObjectMap()
{
Map( m => m.FooString );
Map( m => m.Label );
}
}
public class CsvObject
{
public string Label { get; set; }
public string FooString { get; set; }
}
public class Foo
{
public string Label { get; set; }
public List<Bar> Bars { get; set; }
}
public class Bar
{
public string Label { get; set; }
}
}
Live demo : https://dotnetfiddle.net/SNqZX1
In this exemple I have used CsvHelper for CSV serialisation, and Json.NET for the Json serialisation. Note that Writing a CSV to a file is a more simlpe task that to a string like in this example

Parsing file with CsvHelper that has Extra Fields/Properties Not in the Class

I need to parse incoming CSV files that mostly map to a specific class. However, the clients are permitted to add extra "user defined" fields to the end of record if they wish. So the CSV might look something like:
Id,FirstName,LastName,MyExtraField1,MyExtraField2
1,John,Doe,foo,bar
2,Jane,Smith,foo2,bar2
My class has named properties for Id, FirstName and LastName, but not for MyExtraField1 and MyExtraField2.
If I created a new property on the class called "ExtraFields" that was a Dict is it possible to take any field in the CSV that is not matched to the class and stuff it in the ExtraFields dictionary? The key would be the name of the field from the header and then the value for that record. Or is there some other way to capture these fields that don't map to any property in the class?
I believe this gets what you are looking for.
static void Main(string[] args)
{
using (MemoryStream stream = new MemoryStream())
using (StreamWriter writer = new StreamWriter(stream))
using (StreamReader reader = new StreamReader(stream))
using (CsvReader csv = new CsvReader(reader))
{
writer.WriteLine("Id,FirstName,LastName,MyExtraField1,MyExtraField2");
writer.WriteLine("1,John,Doe,foo,bar");
writer.WriteLine("2,Jane,Smith,foo2,bar2");
writer.Flush();
stream.Position = 0;
csv.Read();
csv.ReadHeader();
var headers = csv.Context.HeaderRecord.ToList();
csv.Configuration.RegisterClassMap(new TestClassMap(headers.Skip(3)));
var records = csv.GetRecords<TestClass>().ToList();
}
}
public class TestClass
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public Dictionary<string, string> ExtraFields { get; set; }
}
public sealed class TestClassMap : ClassMap<TestClass>
{
public TestClassMap(IEnumerable<string> headers)
{
Map(m => m.Id);
Map(m => m.FirstName);
Map(m => m.LastName);
Map(m => m.ExtraFields).ConvertUsing(row => headers.ToDictionary(h => h, h => row.GetField(h)));
}
}

CsvHelper: writing null strings as special string

I'm trying to configure CsvWriter to use special string "#NULL#" for nullable string properties. For reader it works, by setting csvReader.Configuration.TypeConverterOptionsCache.GetOptions<string>().NullValues.Add("#NULL#"); - it reads "#NULL#" fields in csv as null strings.
The code I'm using for writer is below, but it ignores added NullValues and outputs empty strings instead (default behavior).
Is there other config parameter for writer? Thanks.
public class Entity
{
public string Name { get; set; }
public int Id { get; set; }
}
[Test]
public void csv_write_test()
{
var entities = new[] {new Entity {Id = 1, Name = null}, new Entity {Id=2, Name = "SampleName"} };
var fileName = "C:/Temp/tr/recordings/withNulls/sample-test.csv";
File.Delete(fileName);
using (var textWriter = new StreamWriter(fileName))
{
var csvWriter = new CsvWriter(textWriter);
csvWriter.Configuration.TypeConverterOptionsCache.GetOptions<string>().NullValues.Add("#NULL#");
csvWriter.WriteRecords(entities);
}
}
You can use a custom ITypeConverter to accomplish this.
void Main()
{
using (var stream = new MemoryStream())
using (var writer = new StreamWriter(stream))
using (var reader = new StreamReader(stream))
using (var csv = new CsvWriter(writer))
{
var records = new List<Test>
{
new Test { Id = 1, Name = "one" },
new Test { Id = 2, Name = null },
};
csv.Configuration.RegisterClassMap<TestMap>();
csv.WriteRecords(records);
writer.Flush();
stream.Position = 0;
reader.ReadToEnd().Dump();
}
}
public class Test
{
public int Id { get; set; }
public string Name { get; set; }
}
public sealed class TestMap : ClassMap<Test>
{
public TestMap()
{
Map(m => m.Id);
Map(m => m.Name).TypeConverter<CustomNullTypeConverter<string>>();
}
}
public class CustomNullTypeConverter<T> : DefaultTypeConverter
{
public override string ConvertToString(object value, IWriterRow row, MemberMapData memberMapData)
{
if (value == null)
{
return "#NULL#";
}
var converter = row.Configuration.TypeConverterCache.GetConverter<T>();
return converter.ConvertToString(value, row, memberMapData);
}
}
If you want it to use the first value in the NullValues option, you'll need to submit a feature request.

mapping / write assist with csvhelper

Quick help here please on csvhelper...
csv:
Name,LastName
PersonMap:
public override void CreateMap()
{
Map(x => x.Name).Name("Name");
Map(x => x.LasName).Name("LastName");
}
Person Class:
public string Name { get; set; }
public string LastName { get; set; }
Main:
public void writePerson()
{
IEnumerable<Person> records;
using (var r = new CsvReader(new StreamReader("person.csv")))
{
r.Configuration.RegisterClassMap<PersonMap>();
records = r.GetRecords<Person>().ToList();
}
using (var w = new CsvWriter(new StreamWriter("person.csv")))
{
w.Configuration.RegisterClassMap<PersonMap>();
w.WriteRecord(records); //rewrite csv list
w.WriteField("John"));
w.WriteField("Doe");
w.NextRecord();
}
}
ERROR LINE: records = reader.GetRecords().ToList();
ERROR:
No header record was found.
ok so I fixed it with the following:
Write:
string persondata = "John, Doe";
using (FileStream fs = new FileStream("person.csv", FileMode.Append, FileAccess.Write))
using (StreamWriter sw = new StreamWriter(fs))
{ sw.WriteLine(persondata); sw.Dispose(); }
Read:
IEnumerable<Person> records;
using (var reader = new CsvReader(new StreamReader(#"person.csv")))
{
reader.Configuration.RegisterClassMap<PersonMap>();
records = reader.GetRecords<Person>();
}
Looks like your person.csv doesn't have the first line as a header line as follows:
Name,LastName
Please check the CSV file.
Add following configuration line
w.configuration.HasHeaderRecord = false;

Categories

Resources