i am trying to write two different Objects in one row with the C# library CSVHelper.
It should look something like this:
obj1 obj2
-----------|------------
record1 record1
record2 record2
When register the class maps for these two objects and then call WriteRecords(List) and WriteRecords(List) these objects are written but they are not in the same row. Instead the records of obj2 are written in the rows following the records of obj1.
It looks like this:
obj1
----------
record1
record2
obj2
----------
record1
record2
Program.cs:
string fileReadDirectory =
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), "Stuecklisten");
string fileWriteDirectory =
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), "Stueckliste.csv");
List<string> files = Directory.GetFiles(fileReadDirectory).ToList();
List<Part> parts = new List<Part>();
List<PartsPerList> partsPerLists = new List<PartsPerList>();
foreach (string file in files)
{
//Reads records from Excel File
CsvReader reader = new CsvReader(new ExcelParser(file));
reader.Context.RegisterClassMap<ExcelSheetMap>();
IEnumerable<Part>? excelRecords = reader.GetRecords<Part>();
foreach (var record in excelRecords)
{
PartsPerList partsPerList = new PartsPerList();
partsPerList.Listname = file;
if (parts.Any(p => p.ManufacturerNr == record.ManufacturerNr))
{
Part part = parts.SingleOrDefault(p => p.ManufacturerNr == record.ManufacturerNr) ?? new Part();
part.TotalQuantity += record.TotalQuantity;
}
else
{
parts.Add(record);
}
partsPerLists.Add(partsPerList);
}
}
using (var stream = File.Open(fileWriteDirectory, FileMode.Create))
using (var streamWriter = new StreamWriter(stream))
using (var writer = new CsvWriter(streamWriter,CultureInfo.InvariantCulture))
{
writer.Context.RegisterClassMap<ExcelSheetMap>();
writer.Context.RegisterClassMap<ManufacturerPartsMap>();
writer.WriteHeader(typeof(Part));
writer.WriteRecords(parts);
writer.WriteHeader(typeof(PartsPerList));
writer.WriteRecords(partsPerLists);
}
Part.cs:
public class Part
{
// public int Quantity { get; set; }
public int TotalQuantity { get; set; }
public string Description { get; set; } = string.Empty;
public string Designator { get; set; } = string.Empty;
public string Case { get; set; } = string.Empty;
public string Value { get; set; } = string.Empty;
public string Tolerance { get; set; } = string.Empty;
public string Remark { get; set; } = string.Empty;
public string PartNumber { get; set; } = string.Empty;
public string Manufacturer { get; set; } = string.Empty;
public string ManufacturerNr { get; set; } = string.Empty;
public string RoHS { get; set; } = string.Empty;
public string Nachweis { get; set; } = string.Empty;
}
Part Classmap:
public sealed class ExcelSheetMap : ClassMap<Part>
{
public ExcelSheetMap()
{
// Map(m => m.Quantity).Name("Qty per pcs");
Map(m => m.TotalQuantity).Index(0);
Map(m => m.Description).Name("description");
Map(m => m.Designator).Name("designator");
Map(m => m.Case).Name("case");
Map(m => m.Value).Name("value");
Map(m => m.Tolerance).Name("tolerance");
Map(m => m.Remark).Name("remark");
Map(m => m.PartNumber).Name("partnumber");
Map(m => m.Manufacturer).Name("manufacturer");
Map(m => m.ManufacturerNr).Name("Manufactorer number");
Map(m => m.RoHS).Name("RoHS");
Map(m => m.Nachweis).Name("Nachweis");
}
}
PartsPerList.cs:
public class PartsPerList
{
public string Listname { get; set; } = string.Empty;
}
ManufacturersPartsMap.cs:
public class ManufacturerPartsMap : ClassMap<PartsPerList>
{
public ManufacturerPartsMap()
{
Map(m => m.Listname).Name("test").Optional();
}
}
To write two different objects in one row with CSVHelper, you can loop through the records and write them line by line.
void Main()
{
var fooRecords = new List<Foo>
{
new Foo { Id = 1, Name = "one" },
new Foo { Id = 2, Name = "two" },
};
var barRecords = new List<Bar>
{
new Bar { Id = 3, Description = "The first one" },
new Bar { Id = 4, Description = "The secord one" },
};
//using (var writer = new StreamWriter("path\\to\\file.csv"))
using (var csv = new CsvWriter(Console.Out, CultureInfo.InvariantCulture))
{
csv.WriteHeader<Foo>();
csv.WriteHeader<Bar>();
csv.NextRecord();
for (int i = 0; i < fooRecords.Count; i++)
{
csv.WriteRecord(fooRecords[i]);
csv.WriteRecord(barRecords[i]);
csv.NextRecord();
}
}
}
public class Foo
{
[Name("FooId")]
public int Id { get; set; }
public string Name { get; set; }
}
public class Bar
{
[Name("BarId")]
public int Id { get; set; }
public string Description { get; set; }
}
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.
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
I am trying to use the CSVhelper plugin to read an uploaded CSV file. Here is my modelBinder class:
public class SurveyEmailListModelsModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var csv = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
var file = ((csv.RawValue as HttpPostedFileBase[]) ?? Enumerable.Empty<HttpPostedFileBase>()).FirstOrDefault();
if (file == null || file.ContentLength < 1)
{
bindingContext.ModelState.AddModelError(
"",
"Please select a valid CSV file"
);
return null;
}
using (var reader = new StreamReader(file.InputStream))
using (var csvReader = new CsvReader(reader))
{
return csvReader.GetRecords<SurveyEmailListModels>().ToArray();
}
}
}
These are the objects I am trying to map to:
public class SurveyEmailListModels
{
[Key]
[CsvField(Ignore = true)]
public int SurveyEmailListId { get; set; }
[CsvField(Index = 0)]
public int ProgramId { get; set; }
[CsvField(Index = 1)]
public virtual SurveyProgramModels SurveyProgramModels { get; set; }
[CsvField(Index = 2)]
public string SurveyEmailAddress { get; set; }
[CsvField(Index = 3)]
public bool SurveyResponded { get; set; }
}
Inside the Visual Studio debugger I am getting an error:
base {"You must call read on the reader before accessing its data."} CsvHelper.CsvHelperException {CsvHelper.CsvReaderException}
Not used the plugin but the error message seems pretty clear. There must be a Read() function to call before accessing the results. Try changing your code to something like this:
using (var reader = new StreamReader(file.InputStream))
using (var csvReader = new CsvReader(reader))
{
// Use While(csvReader.Read()); if you want to read all the rows in the records)
csvReader.Read();
return csvReader.GetRecords<SurveyEmailListModels>().ToArray();
}
I had similar issue , but sorted out, when I tried the below code
void Main()
{
using (var reader = new StreamReader("path\\to\\file.csv"))
using (var csv = new CsvReader(reader,
System.Globalization.CultureInfo.CreateSpecificCulture("enUS")))
{
var records = csv.GetRecords<Foo>();
}
}
Please note below code wont work with latest versions of CSV Helper
using (var csvReader = new CsvReader(reader))
I have some class that has a property of type List<object> I need to serialize that class to XML file using DataContractSerializer.
The serialization fails on ArgumentException when the object is a List<T>/IEnumerator<T>exception message:
Invalid name character in
'System.Collections.Generic.List`1[[MyProj.Result, MyProj,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]'.
Here is the code sample that fails
The Class that takes the List<object>
[DataContract(IsReference = true)]
public class RecoveryMethodData
{
[DataMember]
public List<object> Parameters { get; set; }
public RecoveryMethodData()
{
Parameters = new List<object>();
}
public static void SerializeToFile(RecoveryMethodData recoveryMethodData, string fileName)
{
var encoding = Encoding.UTF8;
using (var fileWriter = new XmlTextWriter(fileName, encoding))
{
fileWriter.Formatting = Formatting.Indented;
// use SharedTypeResolver for deserializing assistance.
var serializer = new DataContractSerializer(typeof(RecoveryMethodData), null, int.MaxValue, false, true, null, new SharedTypeResolver());
serializer.WriteObject(fileWriter, recoveryMethodData);
}
}
}
Here is the usage:
private void TestSerialization()
{
var methodData = new RecoveryMethodData();
var result = new Result() {Message = "wow", Pass = true, FileName = "somefile "};
methodData.Parameters.Add(result);
methodData.Parameters.Add(true);
var list1 = new List<Result>();
list1.Add(new Result(){FileName = "in list1", Message = "in l 1"});
list1.Add(new Result(){FileName = "in list2", Message = "in l 2"});
methodData.Parameters.Add(list1);
RecoveryMethodData.SerializeToFile(methodData,#"C:\serialization_result.xml");
}
public class Result
{
public string Message { get; set; }
public string FileName { get; set; }
}
If I do not add list1 into the methodData.Parameters there is no problem serializing the methodDatad object.
One big limitation is that I can't know in advance which kind of objects will be added to the Parameters property (that is why it is a list of objects)
In order to DataContractSerializer to serialize an object, it shall know the types of all datamembers. In your case, you do not define a specific type but an object type. Try changing the definition of
public List<object> Parameters { get; set; }
to something like:
public List<IMyObject> Parameters { get; set; }
Note that, all of your objects which you are trying to add to the parameters list shall inherit IMyObject interface.
Update: I refactored your code up to some point (still in a bad shape) and it seems working, please have a try;
public class Tester
{
public Tester()
{
this.TestSerialization();
}
public void SerializeToFile(RecoveryMethodData recoveryMetaData,string fileName)
{
var encoding = Encoding.UTF8;
using (var fileWriter = new XmlTextWriter(fileName, encoding))
{
fileWriter.Formatting = Formatting.Indented;
// use SharedTypeResolver for deserializing assistance.
var serializer = new DataContractSerializer(typeof(RecoveryMethodData),new List<Type>(){typeof(bool),typeof(Result),typeof(List<Result>)});
serializer.WriteObject(fileWriter,recoveryMetaData);
}
}
private void TestSerialization()
{
var methodData = new RecoveryMethodData();
var result = new Result() { Message = "wow", Pass = true, FileName = "somefile " };
methodData.Add(result);
methodData.Add(true);
var list1 = new List<Result>();
list1.Add(new Result() { FileName = "in list1", Message = "in l 1" });
list1.Add(new Result() { FileName = "in list2", Message = "in l 2" });
methodData.Add(list1);
SerializeToFile(methodData, #"C:\serialization_result.xml");
}
}
public class Result
{
public string Message { get; set; }
public string FileName { get; set; }
public bool Pass { get; set; }
}
public class RecoveryMethodData : List<object>
{
}