I have the following class serialized into a file using BinaryFormatter:
[Serializable]
public class TestClass
{
public String ItemTwo { get; set; }
public String ItemOne { get; set; }
}
Using this code:
FileStream fs = new FileStream("DataFile.dat", FileMode.Create);
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(fs, new TestClass{ItemOne = "ItemOne", ItemTwo = "ItemTwo"});
fs.Close();
When deserializing using this code:
FileStream fs = new FileStream("DataFile.dat", FileMode.Open);
BinaryFormatter formatter = new BinaryFormatter();
TestClass addresses = (TestClass)formatter.Deserialize(fs);
fs.Close();
I get everything normally.
However, now I need the class to have some backing fields like so:
[Serializable]
public class TestClass
{
private string _itemTwo;
private string _itemOne;
public String ItemTwo
{
get { return _itemTwo; }
set { _itemTwo = value; }
}
public String ItemOne
{
get { return _itemOne; }
set { _itemOne = value; }
}
}
My problem is that now, for some reason, deserialization from previous version doesn't work anymore. I get the class but the Properties are left null.
I cannot affect the serialization process, or the former class state.
How can I get the file to deserialize to the current class?
If you try to serialize the first version of TestClass the backfields will be serialized automatically by the binary formatter. Auto properties are only syntactic sugar, the compiler transform them in normal properties with backing fields. If you decompile your original console application (or class library) with ILSpy for example you'll see that your private fields are declared as:
.field private string '<ItemOne>k__BackingField'
which is way different from your second declaration with backing fields which yields to:
.field private string _itemOne
An approach would be declaring the ISerializable interface on your TestClass and get the value of the original properties using the information contained in the class SerializationInfo
[Serializable]
public class TestClass : ISerializable
{
private string _itemTwo;
private string _itemOne;
public String ItemTwo
{
get { return _itemTwo; }
set { _itemTwo = value; }
}
public String ItemOne
{
get { return _itemOne; }
set { _itemOne = value; }
}
protected TestClass(SerializationInfo info, StreamingContext context)
{
_itemTwo = info.GetString("<ItemTwo>k__BackingField");
_itemOne = info.GetString("<ItemOne>k__BackingField");
}
[SecurityPermissionAttribute(SecurityAction.Demand,
SerializationFormatter = true)]
public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
{
//Add data to your serialization process here
}
}
so you tell the BinaryFormatter how you want your backing fields to be initialized during deserialization.
Related
I have this class:
[Serializable]
public class ServerResponse
{
public string[] Response { get; set; }
public object Packet { get; set; }
public ServerResponse(string[] response, object packet){
this.Response = response;
this.Packet = packet;
}
public string[] getResponse() { return this.Response; }
public object getPacket() { return this.Packet; }
}
And use the following to convert to JSON:
static void writeToClient(TcpClient client, object message)
{
string json = JsonConvert.SerializeObject(message);
NetworkStream clientStream = client.GetStream();
byte[] buffer = NetworkHelper.GetMessageAsBytes(json);
clientStream.Write(buffer, 0, buffer.Length);
clientStream.Flush();
}
However, the Packet is never serialized. Can anyone see what I'm doing wrong? Sample output:
{"Response":["message","Authenticated"],"Packet":{}}
And this is the actual class I'm attempting to serialize:
[Serializable]
public class ActiveGameSession
{
private LoginServerSettings serverSettings;
private List<UserCharacter> sessionCharacters = new List<UserCharacter>();
private UserAccount sessionAccount;
public ActiveGameSession()
{
this.sessionCharacters = new List<UserCharacter>();
this.sessionAccount = null;
this.serverSettings = null;
}
public ActiveGameSession(List<UserCharacter> chars, UserAccount session)
{
this.sessionCharacters = chars;
this.sessionAccount = session;
}
#region setters
public void setCharacters(List<UserCharacter> chars)
{
this.sessionCharacters = chars;
}
public void setSession(UserAccount session)
{
this.sessionAccount = session;
}
public void setServerSettings(LoginServerSettings settings)
{
this.serverSettings = settings;
}
#endregion
#region getters
public List<UserCharacter> getCharacters()
{
return this.sessionCharacters;
}
public UserAccount getAccount()
{
return this.sessionAccount;
}
public LoginServerSettings getSettings()
{
return this.serverSettings;
}
#endregion
}
JSON.NET may not support serialization of an object of type Object. Try using a more concrete type. If you want to have multiple packet types you can try using generics or derive from a common base class.
Edit:
Also there are no public properties of ActiveGameSession to be deserialized, please note that the getXXX, setXXX methods is not recognized as properties.
You need to cast the object representing the Packet to a type that JSON.NET can serialize. Try casting to a string and try again.
So i need to xml serialize a generic list of objects where the object consists of another list and a string.
This is what i get now
<?xml version="1.0" encoding="UTF-8"?>
-<ArrayOfRecept xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
-<Recept>
<Namn>Nomnom</Namn>
</Recept>
-<Recept>
<Namn>Ännu mer nomnom</Namn>
</Recept>
</ArrayOfRecept>
Here only the string value is serialized and not my List
This is my object that i want to serialize
public class Recept
{
private ListHanterare<string> ingredienser;
private string namn;
public Recept()
{
ingredienser = new ListHanterare<string>();
}
public ListHanterare<string> Ingredienser
{
get { return ingredienser; }
}
public string Namn
{
get { return namn; }
set { namn = value; }
}
}
So I will have a list of Recept that i want to xml serialize and I want the xml to show both "Namn" and the "Ingredienser"-list.
This is my serializer
class XMLSerial
{
public static bool Serialize<T>(T obj, string filePath)
{
bool bok = true;
XmlSerializer serializer = new XmlSerializer(typeof(T));
TextWriter writer = new StreamWriter(filePath);
try
{
serializer.Serialize(writer, obj);
}
catch
{
bok = false;
}
finally
{
if (writer != null)
writer.Close();
}
return bok;
}
}
This is inside the ListHanterare class where I pass the object to the serializer
public bool XMLSerialize(string filePath){
return XMLSerial.Serialize<List<T>>(lista, filePath);
EDIT:
So by adding a setter to ingredienser I now get this
-<Recept>
<Ingredienser/>
<Namn>rec</Namn>
</Recept>
But ingredienser is still empty
My ListHanterare class is a basic generic List class
public class ListHanterare<T> : IListHanterare<T>
{
private List<T> lista; // This is the list
private int count;
public ListHanterare()
{
lista = new List<T>();
}
So i need to serialize a ListHanterare list of Recept objects where the Recept object consists of a string and another ListHanterare list of strings, the string is serialized correctly but not the list of strings.
In order for XmlSerializer to serialize or deserialize the property Ingredienser of your Recept class, it must have a public setter as well as a public getter:
public ListHanterare<string> Ingredienser
{
get { return ingredienser; }
set { ingredienser = value; }
}
If you don't do this, XmlSerializer will ignore the property.
You might still have problems with the class ListHanterare, but it's not shown in your question.
DataContractSerializer does not have this requirement. However, to serialize and deserialize your classes with DataContractSerializer and include private fields or properties, you would need to fully annotate them with data contract attributes. Private field and properties marked with [DataMember] get serialized despite being private. Data contract serialization is opt-in, however, so to use this feature your classes would need to be fully attributed.
Update
You don't show the complete code of your ListHanterare<string> class. In order for XmlSerializer to serialize and deserialize it successfully, it must either:
Make all its contents available in publicly settable and gettable properties, or
Implement ICollection<T> (or even IList<T>) as described here: XmlSerializer Class.
For method #1, you could modify your class as follows:
public class ListHanterare<T>
{
private List<T> lista; // This is the list
private int count;
public ListHanterare()
{
lista = new List<T>();
}
[XmlElement("Item")]
public T[] SerializableList
{
get
{
return (lista == null ? new T [0] : lista.ToArray());
}
set
{
if (lista == null)
lista = new List<T>();
lista.Clear();
lista.AddRange(value ?? Enumerable.Empty<T>());
count = lista.Count;
}
}
}
You will now see the items serialized your XML.
I have this code:
[DataContract]
class MyData
{
private Int32 dato1;
[DataMember]
public Int32 Dato1
{
get { return dato1; }
set { dato1 = value; }
}
public MyData(Int32 dato1)
{
this.dato1 = dato1;
}
public MyData()
{
this.dato1 = 0;
}
}
[DataContract]
class MyCollection2 : List<MyData>
{
public MyCollection2()
{
}
}
Then I try to serialize one object of MyCollection2 with:
MyCollection2 collec2 = new MyCollection2();
collec2.Add(new MyData(10));
DataContractSerializer ds = new DataContractSerializer(typeof(MyCollection2));
using (Stream s = File.Create(dialog.FileName))
{
ds.WriteObject(s, collec2);
}
Then I get the next exception:
InvalidDataContractException is an invalid collection type since it
have DataContractAttribute
However, if I use the next class (doesn't inherit directly from List, instead has a List member):
[DataContract]
class MyCollection1
{
[DataMember]
public List<MyData> items;
public MyCollection1()
{
items = new List<MyData>();
}
}
Here Serialization works ok. Do you know why ?.
Thanks very much.
Use [CollectionDataContract(...)] instead of [DataContract]. For more details see here.
For full details see here.
In my project I'm using a List<Name> for storing data.
Now I wanted to save the List via XMLSerialization:
XmlSerializer listser = new XmlSerializer(typeof(List<Name>)); //Stops here, jumps back to screen/GUI
FileStream liststr = new FileStream(xmlSaveFile_Dialog.FileName, FileMode.Create);
listser.Serialize(liststr, nameslist.list);
liststr.Close();
Now the method simply stops at the XmlSerializer declaration.(There's no exception!)
I'm using exactly the same method before to serialize another object (List<File>).
This works without problems.
Now my code:
Name-Class:
[Serializable()]
public class Name
{
//[XmlElement("name")]
public string name { get; set; }
//[XmlElement("index")]
public string index { get; set; }
public Name(string name, string index)
{
this.name = name;
this.index = index;
}
}
Name-List:
[XmlRoot("Units")]
class Namelist
{
[XmlArray("Unitlist")]
[XmlArrayItem("Unit", typeof(Name))]
public List<Name> list;
// Constructor
public Namelist()
{
list = new List<Name>();
}
public void AddNameData(Name item)
{
list.Add(item);
}
}
In the main I declare this in the constructor:
nameslist = new NameList(); //this a global internal variable
Exactly the same way I did it with the List<File> object...
Name is not XML serializable in its present definition. The XML serializer cannot handle classes that lack a public parameterless ctor. So you should basically include the following ctor to Name:
public Name()
{
}
Hope this helps.
I have a List<object> with different types of objects in it like integers, strings, and custom types. All custom types are protobuf-adjusted.
What I wanna do now is to serialize / deserialize this list with protobuf.net. Up until now I suspect that I have to declare each and every type explicitly, which is unfortunately not possible with these mixed-list constructs. Because the binary formater has no problems to do these things I hope that I missed something and that you can help me out.
So my question is how to deal with objects in protobuf.net.
(disclosure: I'm the author of protobuf-net)
BinaryFormatter is a metadata-based serializer; i.e. it sends .NET type information about every object serialized. protobuf-net is a contract-based serializer (the binary equivalent of XmlSerializer / DataContractSerializer, which will also reject this).
There is no current mechanism for transporting arbitrary objects, since the other end will have no way of knowing what you are sending; however, if you have a known set of different object types you want to send, there may be options. There is also work in the pipeline to allow runtime-extensible schemas (rather than just attributes, which are fixed at build) - but this is far from complete.
This isn't ideal, but it works... it should be easier when I've completed the work to support runtime schemas:
using System;
using System.Collections.Generic;
using ProtoBuf;
[ProtoContract]
[ProtoInclude(10, typeof(DataItem<int>))]
[ProtoInclude(11, typeof(DataItem<string>))]
[ProtoInclude(12, typeof(DataItem<DateTime>))]
[ProtoInclude(13, typeof(DataItem<Foo>))]
abstract class DataItem {
public static DataItem<T> Create<T>(T value) {
return new DataItem<T>(value);
}
public object Value {
get { return ValueImpl; }
set { ValueImpl = value; }
}
protected abstract object ValueImpl {get;set;}
protected DataItem() { }
}
[ProtoContract]
sealed class DataItem<T> : DataItem {
public DataItem() { }
public DataItem(T value) { Value = value; }
[ProtoMember(1)]
public new T Value { get; set; }
protected override object ValueImpl {
get { return Value; }
set { Value = (T)value; }
}
}
[ProtoContract]
public class Foo {
[ProtoMember(1)]
public string Bar { get; set; }
public override string ToString() {
return "Foo with Bar=" + Bar;
}
}
static class Program {
static void Main() {
var items = new List<DataItem>();
items.Add(DataItem.Create(12345));
items.Add(DataItem.Create(DateTime.Today));
items.Add(DataItem.Create("abcde"));
items.Add(DataItem.Create(new Foo { Bar = "Marc" }));
items.Add(DataItem.Create(67890));
// serialize and deserialize
var clone = Serializer.DeepClone(items);
foreach (DataItem item in clone) {
Console.WriteLine(item.Value);
}
}
}
List<YourClass> list;
ProtoBuf.Serializer.Deserialize<List<YourClass>>(filestream);
There is a way of doing this, albeit not a very clean way, by using a wrapper object that utilises another serialisation mechanism that supports arbitrary objects. I am presenting an example below using JSON but, like I said, resorting to a different serialisation tool seems like defeating the purpose of using protobuf:
[DataContract]
public class ObjectWrapper
{
[DataMember(Order = 1)]
private readonly string _serialisedContent;
[DataMember(Order = 2)]
private readonly string _serialisedType;
public object Content { get; private set; }
[UsedImplicitly]
private ObjectWrapper() { }
public ObjectWrapper(object content)
{
_serialisedContent = JsonConvert.SerializeObject(content);
_serialisedType = content.GetType().FullName;
Content = content;
}
[ProtoAfterDeserialization]
private void Initialise()
{
var type = Type.GetType(_serialisedType);
Content = type != null
? JsonConvert.DeserializeObject(_serialisedContent, type)
: JsonConvert.DeserializeObject(_serialisedContent);
}
}
EDIT: This can also be done using C#'s built-in binary serialisation
[DataContract]
public class ObjectWrapper
{
[DataMember(Order = 1)]
private readonly string _serialisedContent;
public object Content { get; private set; }
[UsedImplicitly]
private ObjectWrapper() { }
public ObjectWrapper(object content)
{
using (var stream = new MemoryStream())
{
var formatter = new BinaryFormatter();
formatter.Serialize(stream, content);
stream.Flush();
stream.Position = 0;
_serialisedContent = Convert.ToBase64String(stream.ToArray());
}
}
[ProtoAfterDeserialization]
private void Initialise()
{
var data = Convert.FromBase64String(source);
using (var stream = new MemoryStream(data))
{
var formatter = new BinaryFormatter();
stream.Seek(0, SeekOrigin.Begin);
return formatter.Deserialize(stream);
}
}
}