I'm trying to save a dictionary of Matrix into an Xml file.
My Matrix class attributes are :
public class Matrix
{
public int Lines { get; set; }
public int Columns { get; set; }
public double[,] Elements { get; set; }
public string name { get; set; }
}
After many attempts, I wrote this :
string fileName = dlg.FileName;
Stream writer = new FileStream(fileName,FileMode.Create);
foreach (KeyValuePair<String, Matrix> matrice in CalMat.Calculatrice.listMatrix)
{
XmlSerializer x = new XmlSerializer(matrice.GetType());
x.Serialize(writer, matrice);
}
writer.Close();
If i run this code with one matrix, the file is created, but i only have this sentence written :
<?xml version="1.0"?><KeyValuePairOfStringMatrix xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" /><?xml version="1.0"?>
I think my code is missing something but I don't know what. A write method, I guess.
Thank you for your time!
I don't think the default KeyValuePair is serializable,
try building your own KeyValuePair class:
[Serializable]
[XmlType(TypeName="MyTypeName")]
public struct KeyValuePair<T1, T2>
{
public T1 Key { get; set; }
public T2 Value { get; set; }
}
Using BinaryFormatter this is the code:
[Serializable] // mark with Serializable
public class Matrix
{
public Matrix(string name, int lines, int columns)
{
Name = name;
Lines = lines;
Columns = columns;
Elements = new double[Lines, Columns];
}
public int Lines { get; set; }
public int Columns { get; set; }
public double[,] Elements { get; set; }
public string Name { get; set; }
}
public static void Main()
{
var path = #"D:\serialize.data"; // use the path that you want
// this is an example collection
var listMatrix = new Dictionary<string, Matrix>();
listMatrix.Add("matrix_1", new Matrix("Matrix 1", 1, 2));
listMatrix.Add("matrix_2", new Matrix("Matrix 2", 2, 2));
// Serialization
var stream = new FileStream(path, FileMode.Create);
var binaryFormatter = new BinaryFormatter();
binaryFormatter.Serialize(stream, listMatrix);
stream.Close();
// Deserialization
stream = new FileStream(path, FileMode.Open);
var result = (Dictionary<string, Matrix>)binaryFormatter.Deserialize(stream);
stream.Close();
}
Using XmlSerializer this is the code:
// I implement my custom KeyValuePair to serialize (because XmlSerializer can not serialize the .net KeyValuePair)
public struct CustomKeyValuePair<T1, T2>
{
public CustomKeyValuePair(T1 key, T2 value): this()
{
Key = key;
Value = value;
}
public T1 Key { get; set; }
public T2 Value { get; set; }
// here I specify how is the cast
public static explicit operator CustomKeyValuePair<T1, T2>(KeyValuePair<T1, T2> keyValuePair)
{
return new CustomKeyValuePair<T1, T2>(keyValuePair.Key, keyValuePair.Value);
}
}
// Matrix class used to Serialize with XmlSerailzer
public class Matrix
{
public Matrix() { } // need a default constructor
public Matrix(string name, int lines, int columns)
{
Name = name;
Lines = lines;
Columns = columns;
Elements = new double[Columns][];
for (int i = 0; i < Elements.Length; i++)
{
Elements[i] = new double[Columns];
}
}
public int Lines { get; set; }
public int Columns { get; set; }
public double[][] Elements { get; set; } // I use double[][] because XmlSerialzer can not serialize a two-dimensional array (double[,])
public string Name { get; set; }
}
public static void Main()
{
var path = #"D:\serialize.data"; // use the path that you want
// this is an example collection
var listMatrix = new Dictionary<string, Matrix>();
listMatrix.Add("matrix_1", new Matrix("Matrix 1", 1, 2));
listMatrix.Add("matrix_2", new Matrix("Matrix 2", 2, 2));
// Serialization
var stream = new FileStream(path, FileMode.Create);
var xmlSerializer = new XmlSerializer(typeof(CustomKeyValuePair<string, Matrix>[]));
var aux = listMatrix.Select(keyValuePair => (CustomKeyValuePair<string, Matrix>) keyValuePair).ToArray();
xmlSerializer.Serialize(stream, aux); // I serialize an array to make easy the deserailizer
stream.Close();
// Deserialization
stream = new FileStream(path, FileMode.Open);
var result = (CustomKeyValuePair<string, Matrix>[])xmlSerializer.Deserialize(stream);
stream.Close();
}
Related
I am trying to create a log file in json format from a List.
my class for list is
public class ChunkItem
{
public int start { get; set; }
public int end { get; set; }
}
public class DownloadItem
{
public int id { get; set; }
public string fname { get; set; }
public string downloadPath { get; set; }
public int chunkCount { get; set; }
public ChunkItem[] chunks { get; set; }
public DownloadItem(int _id, string _fname, string _downloadPath, int _chunkCount, ChunkItem[] _chunks)
{
id = _id;
fname = _fname;
downloadPath = _downloadPath;
chunkCount = _chunkCount;
chunks = _chunks;
}
}
creating a json file from this class works fine
ChunkItem[] chunks = new ChunkItem[2];
chunks[0] = new ChunkItem();
chunks[0].start = 0;
chunks[0].end = 0;
chunks[1] = new ChunkItem();
chunks[1].start = 0;
chunks[1].end = 0;
List<DownloadItem> lst = new List<DownloadItem>();
lst.Add(new DownloadItem(0, "", "", 2, chunks));
lst.Add(new DownloadItem(1, "aaa", "sss", 2, chunks));
lst.Add(new DownloadItem(2, "bbb", "ddd", 2, chunks));
string json = JsonConvert.SerializeObject(lst);
System.IO.File.WriteAllText(logPath, json);
I want to read the file to same class list and do some updates or add new lines
I can read the file to a string but cannot create a new list
how can I convert string (read json file) to List<DownloadItem> new list
You need to read all the contends from the file and deserialize the json string to List<DownloadItem>
var jsonData = File.ReadAllText(filePath)
var list = JsonConvert.DeserializeObject<List<DownloadItem>>(jsonData);
Clas DownloadItem is missing a default parameterless constructor.
I use Newtonsoft, where creating the instances and filling them is simple
var result = Newtonsoft.Json.JsonConvert.DeserializeObject<MyClass>(jsonString);
Question:
Given to object FooBar that contains a List of Bar, with FooBar and Bar define as such:
class FooBar{
int FooID {get;set;}
string FooProperty1 {get;set;}
List<Bar> Bars {get;set;};
}
class Bar{
int BarID {get;set;}
string BarProperty1 {get;set;}
string BarProperty2 {get;set;}
string BarProperty3 {get;set;}
}
I get the following CSV as input:
1,FooProperty1,BarID_1,BarProperty1_1,BarProperty2_1,BarProperty3_1,BarID_2,BarProperty1_2,BarProperty2_2,BarProperty3_2
Where the field BarID, BarProperty1, BarProperty2, BarProperty3 are suffixed by their indice in the collection.
How do I deserialise this input into my object?
input Exemple:
1 instance of FooBar, and 2 sub Bar: 1,FooProperty1,BarID_1,BarProperty1_1,BarProperty2_1,BarProperty3_1,BarID_2,BarProperty1_2,BarProperty2_2,BarProperty3_2
1 instance of FooBar but no Bar:
1,FooProperty1
Tries:
I have try using Convert in order to map those property to a new instance of Bar like :
public class FooBarMap : ClassMap<FooBar>
{
public FooBarMap()
{
Map(m => m.FooID);
Map(m => m.Bars).ConvertUsing(row =>
{
var list = new List<Bar>
{
new Bar {
BarProperty1 = row.GetField("BarProperty1_1"),
BarProperty2 = row.GetField("BarProperty2_1"),
// .. Other Properties
},
new Bar {}, //.. Same on _2
};
return list;
});
}
}
Of course no control over the input. I would have been sending Json/Xml not CSV.
Its possible with a custom type converter but tricky.
You need to decorate the property with an Index attribute (even though it's not used)
public class FooBar
{
[Index(2)]
public List<Bar> Bars { get; set; }
}
The converter is used for both reading and writing, so you need to override two methods:
public class BarListConverter : DefaultTypeConverter
{
public override object ConvertFromString(string text, IReaderRow row, MemberMapData memberMapData)
{
var list = new List<Bar>();
if (text == null) return list;
do
{
var barIndex = list.Count + 1;
var bar = new Bar
{
BarID = row.GetField<int>($"BarID_{barIndex}"),
BarProperty1 = row.GetField<string>($"BarProperty1_{barIndex}"),
BarProperty2 = row.GetField<string>($"BarProperty2_{barIndex}"),
BarProperty3 = row.GetField<string>($"BarProperty3_{barIndex}")
};
list.Add(bar);
} while (row.Context.CurrentIndex > 0 && row.Context.CurrentIndex < row.Context.Record.Length - 1);
return list;
}
public override string ConvertToString(object value, IWriterRow row, MemberMapData memberMapData)
{
var bars = value as List<Bar>;
if (bars == null) return null;
foreach (var bar in bars)
{
row.WriteField(bar.BarID);
row.WriteField(bar.BarProperty1);
row.WriteField(bar.BarProperty2);
row.WriteField(bar.BarProperty3);
}
return null;
}
}
}
Reading:
public List<FooBar> Reading()
{
using (var stream = new MemoryStream())
using (var writer = new StreamWriter(stream))
using (var reader = new StreamReader(stream))
using (var csv = new CsvReader(reader))
{
writer.WriteLine(
"FooID,FooProperty1,BarID_1,BarProperty1_1,BarProperty2_1,BarProperty3_1,BarID_2,BarProperty1_2,BarProperty2_2,BarProperty3_2");
writer.WriteLine("1,Foo1,1,2,3,4,5,6,7,8");
writer.Flush();
stream.Position = 0;
csv.Configuration.HeaderValidated = null;
csv.Configuration.MissingFieldFound = null;
csv.Configuration.TypeConverterCache.AddConverter<List<Bar>>(new BarListConverter());
return csv.GetRecords<FooBar>().ToList();
}
}
Writing:
public string Writing(List<FooBar> data)
{
using (var stream = new MemoryStream())
using (var writer = new StreamWriter(stream))
using (var reader = new StreamReader(stream))
using (var csv = new CsvWriter(writer))
{
csv.Configuration.HasHeaderRecord = false;
csv.Configuration.TypeConverterCache.AddConverter<List<Bar>>(new BarListConverter());
csv.WriteRecords(data);
writer.Flush();
stream.Position = 0;
return reader.ReadToEnd();
}
I have made a very simplified method that will iterate over the key/value pairs...
private static FooBar Parse(string value)
{
// a basic check for null or empty string
if (String.IsNullOrEmpty(value)) throw new ArgumentNullException(nameof(value));
// split the string
string[] split = value.Split(',');
// a basic check for properly formed key/value pairs in the string
if (split.Length < 2 || split.Length % 2 != 0)
throw new ArgumentException("Malformed string.", nameof(value));
// put the values into our object
var result = new FooBar();
// Foo pair
result.FooID = Int32.Parse(split[0]);
result.FooProperty1 = split[1];
// Bar pairs
result.Bars = new List<Bar>();
for (int i = 2; i < split.Length; i = i + 4)
{
result.Bars.Add(new Bar()
{
BarID = split[i],
BarProperty1 = split[i+1],
BarProperty2 = split[i+2],
BarProperty3 = split[i+3]
});
}
return result;
}
I ran this against the two examples like so
string value = "1,FooProperty1";
FooBar result = Parse(value); // works
and
string value = "1,FooProperty1,BarID_1,BarProperty1_1,BarProperty2_1,BarProperty3_1,BarID_2,BarProperty1_2,BarProperty2_2,BarProperty3_2";
FooBar result = Parse(value); // works
note that your class needs to be slightly updated to
public class FooBar
{
public int FooID { get; set; }
public string FooProperty1 { get; set; }
public List<Bar> Bars { get; set; }
}
public class Bar
{
public string BarID { get; set; } // you originally had this as int
public string BarProperty1 { get; set; }
public string BarProperty2 { get; set; }
public string BarProperty3 { get; set; }
}
To me, this option seems far less convoluted and, easier to read overall. I don't particularly see any reason that you have to force the conversion to be done via other means such as using the CSVHelper class.
I'm not being able to deserialize a collection of elements where the instances have a Inheritance relationship between them.
Does anyone came across this issue?
So my use case is this:
My model is similiar to this:
[DataContract]
public class Item
{
[DataMember]
public int Id { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public bool Valid { get; set; }
}
[DataContract]
public class IntermediateItem : Item
{
[DataMember]
public int Priority { get; set; }
}
[DataContract]
public class ExtendedItem : IntermediateItem
{
[DataMember]
public int Count { get; set; }
[DataMember]
public ItemsCollection Childs { get; set; }
}
And Items Collection is something like this:
[DataContract]
public class ItemsCollection : Collection<Item>
{
}
The setup that I have made to ensure the proper deserialization is:
Defining the CollectionFormatterBase:
public class ItemCollectionFormatterBase : CollectionFormatterBase<Item, ItemsCollection>
{
protected override ItemsCollection Create(int count)
{
return new ItemsCollection();
}
protected override void Add(ItemsCollection collection, int index, Item value)
{
collection.Add(value);
}
}
The example that is not working, and not working I mean, the deserialized instances are all of base type, some how the inheritance relationship got lost in the serialization.
Example:
MessagePack.Resolvers.CompositeResolver.RegisterAndSetAsDefault(new[] { new ItemCollectionFormatterBase() }, new[] { StandardResolver.Instance });
ExtendedItem instance = new ExtendedItem()
{
Id = 1,
Name = "Extended Item",
Priority = 121,
Valid = true,
Count = 10,
Childs = new ItemsCollection(new List<Item>() { new Item() { Id = 1 }, new IntermediateItem() { Priority = 10 }, new ExtendedItem() { Count = 10 } })
};
byte[] bytes = MessagePackSerializer.Serialize(instance);
using (FileStream file = new FileStream(this.filePath.AbsolutePath, FileMode.Create))
{
await file.WriteAsync(bytes , 0, payload.Length);
await file.FlushAsync();
}
using (FileStream file = new FileStream(testsFolder + #"\ExtendedItem.msgPack-csharp.dat", FileMode.Open))
{
file.Seek(0, SeekOrigin.Begin);
deserializedInstance = MessagePackSerializer.Deserialize<ExtendedItem>(file);
}
looking at the deserializedInstance Childs elements they all are from Item Type.
Can you tell me what I'm doing wrong ? What is missing ?
A small update regarding Item definition:
[DataContract]
[KnownType(typeof(IntermediateItem))]
[KnownType(typeof(ExtendedItem))]
public class Item
{
[DataMember]
public int Id { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public bool Valid { get; set; }
}
This also does not work. :(
Well looks like MessagePackSerializer static type as a static inner class called Typeless and that solve my problem:
With a instance of a ExtendedItem:
ExtendedItem instance = new ExtendedItem()
{
Id = 1,
Name = "Extended Item",
Priority = 121,
Valid = true,
Count = 10,
Childs = new ItemsCollection(new List<Item>() { new Item() { Id = 1 }, new IntermediateItem() { Priority = 10 }, new ExtendedItem() { Count = 10 } })
};
I was able to serialize that and deserialize with success !
byte[] bytes = MessagePackSerializer.Typeless.Serialize(instance);
await fileManager.WriteAsync(bytes);
ExtendedItem deserializedInstance = null;
deserializedInstance = MessagePackSerializer.Typeless.Deserialize(bytes) as ExtendedItem;
despite the serialization and deserialization worked on .NET this sample did not work when deserializing in nodejs with msgpackjs package.
Maybe i misunderstood something for serialization. i wanna serialize my .net object fastest way. i made some googling i found protobuf. Myfirstquestion is ProtoBuf.Net has avility for xml serailization.if it has, can i use it for xml serialization.
My model:
[XmlType]
public class CT {
[XmlElement(Order = 1)]
public int Foo { get; set; }
}
[XmlType]
public class TE {
[XmlElement(Order = 1)]
public int Bar { get; set; }
}
[XmlType]
public class TD {
[XmlElement(Order = 1)]
public List CTs { get; set; }
[XmlElement(Order = 2)]
public List TEs { get; set; }
[XmlElement(Order = 3)]
public string Code { get; set; }
[XmlElement(Order = 4)]
public string Message { get; set; }
[XmlElement(Order = 5)]
public DateTime StartDate { get; set; }
[XmlElement(Order = 6)]
public DateTime EndDate { get; set; }
}
my serializer :
CT ct = new CT() { Foo = 1 };
List<CT> list = new List<CT>();
list.Add(ct);
TE te = new TE() { Bar = 2 };
List<TE> list2 = new List<TE>();
list2.Add(te);
TD td = new TD() { Code = "Test",Message = "Yusuf",StartDate = DateTime.Now,EndDate = DateTime.Now,CTs = list,TEs = list2 };
List<TD> list3 = new List<TD>();
list3.Add(td);
Stopwatch stopwatch5 = new Stopwatch();
stopwatch5.Start();
string str = String.Empty;
using (MemoryStream stream = new MemoryStream())
{
byte[] data = Serialize(list3);
XmlDocument doc = new XmlDocument();
string xml = Encoding.UTF8.GetString(data); <--SHOULD CREATE XML
doc.LoadXml(xml);
// str = Convert.ToBase64String(stream.GetBuffer(),0,(int)stream.Length);
}
stopwatch5.Stop();
Console.WriteLine(((double)(stopwatch5.Elapsed.TotalMilliseconds * 1000000) / 1000000).ToString("0.00 ns"));
Console.Read();
}
public static byte[] Serialize(List<TD> tData) {
using(var ms = new MemoryStream()) {
ProtoBuf.Serializer.Serialize(ms,tData);
return ms.ToArray();
}
}
public static List<TD> Deserialize(byte[] tData) {
using(var ms = new MemoryStream(tData)) {
return ProtoBuf.Serializer.Deserialize<List<TD>>(ms);
}
}
it should create xml as a result of " string xml = Encoding.UTF8.GetString(data);". But doesn't. How can i produxe xml result?
Protocol buffers doesn't serialize objects to XML.
It produces binary data. And it has its own set of attributes.
Check this answer
Is Protobuf-net's serialization/deserialization faster than XML ? Yes, by far.
However XmlSerializer is fast enough for most of the tasks.
What you should remind when using it though, is:
XmlSerializer instance is generating code on the fly and compile this code into an assembly.
This generated assembly is then used to serialize and deserialize your objects really fast.
So you should cache instances of XmlSerializer (and avoid recreating them)
you could add a warm up by calling Serialize and Deserialize in order to initialize inner objects and jit them.
You could even go further by generating the auto-generated assembly by yourself, but then you should remember to regenerate each time you change the objects (It can be automated with a MsBuild Task).
You can also look further optimizations:
On Stack Overflow
With Sgen
You can only have one tag at the root level of your xml. So either TD cannot be a list, or you must have an outer tag around the List. This code works
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
TD td = new TD()
{
Code = "Test",
Message = "Yusuf",
StartDate = DateTime.Now,
EndDate = DateTime.Now,
CTs = new List<CT>() {
new CT() { Foo = 1},
new CT() { Foo = 2},
new CT() { Foo = 3},
},
TEs = new List<TE>() {
new TE() { Bar = 1},
new TE() { Bar = 2},
new TE() { Bar = 3},
}
};
using (MemoryStream stream = new MemoryStream())
{
byte[] data = Serialize(td);
XmlDocument doc = new XmlDocument();
string xml = Encoding.UTF8.GetString(data);
doc.LoadXml(xml);
// str = Convert.ToBase64String(stream.GetBuffer(),0,(int)stream.Length);
}
}
public static byte[] Serialize(TD tData)
{
using (var ms = new MemoryStream())
{
XmlSerializer serializer = new XmlSerializer(typeof(TD));
serializer.Serialize(ms, tData);
return ms.ToArray();
}
}
public static TD Deserialize(byte[] tData)
{
using (var ms = new MemoryStream(tData))
{
XmlSerializer xs = new XmlSerializer(typeof(TD));
return (TD)xs.Deserialize(ms);
}
}
}
[XmlRoot("CT")]
public class CT
{
[XmlElement(ElementName = "Foo", Order = 1)]
public int Foo { get; set; }
}
[XmlRoot("TE")]
public class TE
{
[XmlElement(ElementName = "Bar", Order = 1)]
public int Bar { get; set; }
}
[XmlRoot("TD")]
public class TD
{
[XmlElement(ElementName = "CTs", Order = 1)]
public List<CT> CTs { get; set; }
[XmlElement(ElementName = "TEs", Order = 2)]
public List<TE> TEs { get; set; }
[XmlElement(ElementName = "Code", Order = 3)]
public string Code { get; set; }
[XmlElement(ElementName = "Message", Order = 4)]
public string Message { get; set; }
[XmlElement(ElementName = "StartDate", Order = 5)]
public DateTime StartDate { get; set; }
[XmlElement(ElementName = "EndDate", Order = 6)]
public DateTime EndDate { get; set; }
}
}
public class Placement
{
public Point3D Location { get; set; }
public Point3D Axis { get; set; }
public Point3D Direction { get; set; }
}
public class Attribute1
{
public string Key { get; set; }
public Type Type { get; set; }
public Object Value { get; set; }
}
class Program
{
static void Main(string[] args)
{
Attribute1 a = new Attribute1();
a.Key = "test";
var p = new Placement();
p.Axis = new Point3D(12.0, 22.09, 0);
p.Location = new Point3D(12.0, 22.09, 0);
p.Direction = new Point3D(12.0, 22.09, 0);
a.Value = p;
var serializer = new XmlSerializer(typeof(Attribute1));
var path = "E:\\details.xml";
using (TextWriter writer = new StreamWriter(path))
{
serializer.Serialize(writer, a);
}
Console.Read();
}
}
I am trying to serialize Point3D using XmlSerializer.
I am using XmlSerializer for serializing other properties of the class which contain Attribute1
But I get error while serializing.
Please let me know how to achieve this or point me to relevant resources. Thanks!
You have told it that you want to serialize Attribute1 but not Placement. Just change this one line:
var serializer = new XmlSerializer(typeof(Attribute1), new Type[] { typeof(Placement) });