I am new to XML serialization and I have read that private variables cannot be serialized until they are given under a public property. But while debugging after deserializing I am able to find the private variables also in the deserialized object. Can someone explain this? Here is my code:
class Program
{
static void Main(string[] args)
{
XmlSerializer xs = new XmlSerializer(typeof(Nokia));
Nokia n = new Nokia();
using (Stream s = new FileStream("XMLFile", FileMode.Create, FileAccess.Write, FileShare.None))
{
xs.Serialize(s, n);
}
XmlSerializer xs1 = new XmlSerializer(typeof(Nokia));
using (Stream ds = File.OpenRead("XMLFile"))
{
Nokia dn = (Nokia)xs1.Deserialize(ds);
}
}
}
public class Mobile
{
public int Height = 10;
private int weight = 20;
public Mobile() {}
}
public class Nokia : Mobile
{
public string Signal = "Poor";
public Nokia() {}
}
While debugging when I quick watch my object after deserialization I am able to find the variable weight in the base. How is it possible? Or am I wrong somewhere else?
The private variables will still exist in the deserialized object, but their values will not be stored in the XML serialized version.
To demonstrate this, if you create an instance of your object, change the weight value then serialize it to XML. If you deserialize it, the value of weight in the deserialized object will be the default value, rather than the value set on the original object.
Related
I have a host of classes including the normal designs of object hierarchies and interfaces, base classes, etc. from a project where I cannot modify any code. I have another payload class in my project which uses composition to encapsulate information from the other classes and contain properties in the payload class whose types are the classes from the other project.
Now I have a need to be able to create an instance of the payload class containing instances of those other classes and serialize it to Base64 string for transmission.
Issue is since I cannot touch the code for the other classes, I cannot add serialization attributes (for .NET binary formatter/protobuf-net). I was also trying to look at using protobuf-net without attributes, but since the object hierarchy is too deep, it seemed to be too complicated to create.
Can somebody tell me a better choice to go ahead with the serialization/deserialization part without modifying the existing code.
Sample code to illustrate the requirement is shown below:
void Main()
{
var b = new B();
b.SetStatus("This is B");
var c = new C { Name = "C", Value = 100};
var payload = new Payload(b, c);
var serializedData = SerializeToString<Payload>(payload);
serializedData.Dump();
}
private static TData DeserializeFromString<TData>(string settings)
{
byte[] b = Convert.FromBase64String(settings);
using (var stream = new MemoryStream(b))
{
var formatter = new BinaryFormatter();
stream.Seek(0, SeekOrigin.Begin);
return (TData)formatter.Deserialize(stream);
}
}
private static string SerializeToString<TData>(TData settings)
{
using (var stream = new MemoryStream())
{
var formatter = new BinaryFormatter();
formatter.Serialize(stream, settings);
stream.Flush();
stream.Position = 0;
return Convert.ToBase64String(stream.ToArray());
}
}
public class A
{
public string Id { get; set; }
public string StatusMsg {get;protected set;}
public virtual void SetStatus(string msg)
{
StatusMsg = msg;
}
}
public class B : A
{
public B()
{
Id = new Guid().ToString();
}
public override void SetStatus(string msg)
{
base.SetStatus(msg);
}
}
public class C
{
public string Name
{
get;
set;
}
public Int32 Value
{
get;
set;
}
}
public class Payload
{
public B PropertyB { get; set; }
public C PropertyC { get; set; }
public Payload(B b, C c)
{
this.PropertyB = b;
this.PropertyC = c;
}
}
Without adding configuration attributes, you have a few options:
use a serialize that won't care: XmlSerializer or Json.NET might work if you're lucky
accept the runtime config work of something like protobuf-net or one of the others; it probably isn't as much work as you expect (heck, drop me an email with real code and I might be able to do it)
wrote the serialization entirely manually
write a DTO layer - a basic model with attributes etc that works well with your chosen serializer - and either write code to map between the two models, or use a tool like auto-mapper
Personally, when serialization configuration gets tricky, my default option is "write a DTO model".
I don't understand why protobuf would not work but you can do the serialization manually if you don't mind.
In that case, create a BinaryWriter and then write everything you need to be able to deserialize it back again using a BinaryReader. Doing it this way you will get a very compact representation tailored for you specific needs which is also very fast. But it's a bit more work also. General purpose serialization can be verbose and slow but is almost always preferred in any case.
But I still don't get why using any existing attribute-less serialization method wouldn't work. You should probably start by really understand why it doesn't work with for instance protobuf or JSON.NET. You mentioned "too deep hierarchy". How deep is that?
I'd like to get additional information from the DataContractJsonSerializer.
When I deserialize an object:
var deserializer = new DataContractJsonSerializer(toType);
return deserializer.ReadObject(stream);
I don't get any errors if some useless properties have been added in the json.
I don't retrieve any usable information like missing property with the specific type we are talking about.
And some other things.
Do you guys know how I could retrieve those kind of information?
Update
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
}
class Program
{
private static void Main(string[] args)
{
var json = "{'Id': 1, 'Salary': 100 }";
var settings = new JsonSerializerSettings
{
Error = Error,
MissingMemberHandling = MissingMemberHandling.Error
};
var person = JsonConvert.DeserializeObject<Person>(json, settings);
Console.ReadKey();
}
private static void Error(object sender, Newtonsoft.Json.Serialization.ErrorEventArgs errorEventArgs)
{
Console.WriteLine(errorEventArgs.ErrorContext.Error.Message);
errorEventArgs.ErrorContext.Handled = true;
}
}
End of update
I think it can't do what you need. I would use Json.NET instead of DataContractJsonSerializer. It has such capabilities:
http://www.newtonsoft.com/json/help/html/P_Newtonsoft_Json_JsonSerializerSettings_MissingMemberHandling.htm
It throws an exception if your json has different properties that class.
You can easily install it via NuGet.
If it were me, I would deserialize this way
var json = JsonConvert.DeserializeObject<ExpandoObject>(sampleJson);
This gives you access to all of the data which you can query using
json.ContainsKey('foo');
To see if a property exist.
Or
json.GetEnumerator()
To allow you to loop through all of the elements.
I'm trying to serialize an object to an XML file, but am getting the above error.
The problem seems to be with objects that contain a list of a base class but is populated by objects derived from the base class.
Example code is as follows:
public class myObject
{
public myObject()
{
this.list.Add(new Sw());
}
public List<Units> list = new List<Units>();
}
public class Units
{
public Units()
{
}
}
public class Sw : Units
{
public Sw();
{
}
public void main()
{
myObject myObject = new myObject();
XmlSerializer serializer = new XmlSerializer(typeof(myObject));
TextWriter textWriter = new StreamWriter ("file.xml");
serializer.Serialize (textWriter, myObject);
}
E.g. an object that contains only a List<Units> which is populated by derived objects which inherit from the Units class (Sw).
Sorry for not providing my actual code but the objects involved are quite complex and this seems to be the only part of the object which wont successfully be serialized - and only when the list contains the derived classes.
How can I correctly serialize a class like this?
Mark Units class with XmlInclude attribute passing your derived class as parameter:
[XmlInclude(typeof(Sw))]
public class Units
{
public Units()
{
}
}
To Serialize an object to XML. you can use the following code
public String SerializeResponse(SW sw)
{
try
{
String XmlizedString = null;
XmlSerializer xs = new XmlSerializer(typeof(SW));
//create an instance of the MemoryStream class since we intend to keep the XML string
//in memory instead of saving it to a file.
MemoryStream memoryStream = new MemoryStream();
//XmlTextWriter - fast, non-cached, forward-only way of generating streams or files
//containing XML data
XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8);
//Serialize emp in the xmlTextWriter
xs.Serialize(xmlTextWriter, sw);
//Get the BaseStream of the xmlTextWriter in the Memory Stream
memoryStream = (MemoryStream)xmlTextWriter.BaseStream;
//Convert to array
XmlizedString = UTF8ByteArrayToString(memoryStream.ToArray());
return XmlizedString;
}
catch (Exception ex)
{
throw;
}
}
The method will return an XML String and to make the above function you need to import the following libraries:
using System.Xml;
using System.Xml.Serialization;
using System.IO;
I go to the MSDN to search the singleton serialization, and find the source code http://msdn.microsoft.com/en-us/library/system.runtime.serialization.serializationinfo.aspx
but I had problems when I changed some part of the code. I divide it into two program : serialization and deserialization. Below is the Main function of the two(other classes are in the msdn documentation, link is at the second line).
Serialization program is below:
public static void Main()
{
FileStream fs = new FileStream("DataFile.dat", FileMode.Create);
try
{
// Construct a BinaryFormatter and use it
// to serialize the data to the stream.
BinaryFormatter formatter = new BinaryFormatter();
// Create an array with multiple elements refering to
// the one Singleton object.
Singleton[] a1 = { Singleton.GetSingleton(), Singleton.GetSingleton() };
a1[0].SomeNumber = 555;
formatter.Serialize(fs, a1);
}
catch (SerializationException e)
{
Console.WriteLine("Failed to serialize. Reason: " + e.Message);
throw;
}
finally
{
fs.Close();
}
}
Deserialization program:
public static void Main()
{
FileStream fs = new FileStream("DataFile.dat", FileMode.Open);
try
{
// Construct a BinaryFormatter and use it
// to serialize the data to the stream.
BinaryFormatter formatter = new BinaryFormatter();
Singleton[] a2 = (Singleton[]) formatter.Deserialize(fs);
// This displays "True".
Console.WriteLine(a2[0].SomeNumber);
}
catch (SerializationException e)
{
Console.WriteLine("Failed to serialize. Reason: " + e.Message);
throw;
}
finally
{
fs.Close();
}
}
After I executed the two program, the console didn't print 555 but still 123, why? I can't figure it out and got stuck in it for the whole day, can anybody help me ?
I believe you will find many answers on this documentation : IObjectReference
Serializing Singletons never is really easy. But do you really have to serialize them, and then deserialize them ?
Here is a code sample from the MSDN link I provided :
using System;
using System.Web;
using System.IO;
using System.Collections;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization;
using System.Security.Permissions;
// There should be only one instance of this type per AppDomain.
[Serializable]
[PermissionSet(SecurityAction.Demand, Name="FullTrust")]
[AspNetHostingPermission(SecurityAction.LinkDemand,
Level=AspNetHostingPermissionLevel.Minimal)]
public sealed class Singleton : ISerializable
{
// This is the one instance of this type.
private static readonly Singleton theOneObject = new Singleton();
// Here are the instance fields.
private string someString_value;
private Int32 someNumber_value;
public string SomeString
{
get{return someString_value;}
set{someString_value = value;}
}
public Int32 SomeNumber
{
get{return someNumber_value;}
set{someNumber_value = value;}
}
// Private constructor allowing this type to construct the Singleton.
private Singleton()
{
// Do whatever is necessary to initialize the Singleton.
someString_value = "This is a string field";
someNumber_value = 123;
}
// A method returning a reference to the Singleton.
public static Singleton GetSingleton()
{
return theOneObject;
}
// A method called when serializing a Singleton.
[SecurityPermissionAttribute(SecurityAction.LinkDemand,
Flags=SecurityPermissionFlag.SerializationFormatter)]
void ISerializable.GetObjectData(
SerializationInfo info, StreamingContext context)
{
// Instead of serializing this object,
// serialize a SingletonSerializationHelp instead.
info.SetType(typeof(SingletonSerializationHelper));
// No other values need to be added.
}
// Note: ISerializable's special constructor is not necessary
// because it is never called.
}
[Serializable]
[PermissionSet(SecurityAction.Demand, Name="FullTrust")]
[SecurityPermissionAttribute(SecurityAction.LinkDemand,
Flags=SecurityPermissionFlag.SerializationFormatter)]
[AspNetHostingPermission(SecurityAction.LinkDemand,
Level=AspNetHostingPermissionLevel.Minimal)]
internal sealed class SingletonSerializationHelper : IObjectReference
{
// This object has no fields (although it could).
// GetRealObject is called after this object is deserialized.
public Object GetRealObject(StreamingContext context)
{
// When deserialiing this object, return a reference to
// the Singleton object instead.
return Singleton.GetSingleton();
}
}
class App
{
[STAThread]
static void Main()
{
FileStream fs = new FileStream("DataFile.dat", FileMode.Create);
try
{
// Construct a BinaryFormatter and use it
// to serialize the data to the stream.
BinaryFormatter formatter = new BinaryFormatter();
// Create an array with multiple elements refering to
// the one Singleton object.
Singleton[] a1 = { Singleton.GetSingleton(), Singleton.GetSingleton() };
// This displays "True".
Console.WriteLine(
"Do both array elements refer to the same object? " +
(a1[0] == a1[1]));
// Serialize the array elements.
formatter.Serialize(fs, a1);
// Deserialize the array elements.
fs.Position = 0;
Singleton[] a2 = (Singleton[]) formatter.Deserialize(fs);
// This displays "True".
Console.WriteLine("Do both array elements refer to the same object? "
+ (a2[0] == a2[1]));
// This displays "True".
Console.WriteLine("Do all array elements refer to the same object? "
+ (a1[0] == a2[0]));
}
catch (SerializationException e)
{
Console.WriteLine("Failed to serialize. Reason: " + e.Message);
throw;
}
finally
{
fs.Close();
}
}
}
Okay, so first, to answer your main question:
Your implementation for ISerializable.GetObjectData is using IObjectReference.GetRealObject to fetch a reference to an object already existing on your class loader heap. In other words, no deserialization isn't creating an object and using the setters to populate the serialized data for you - it's just reaching into memory and giving you the instance you modified to '555'.
This works in the sample because everything's happening in the same process - your "deserialization" simply gets a reference to the Singleton instance that you modified earlier. When you split off the deserialize into another program, it fetches the only 'Singleton' reference it knows - the static instance assigned to theOneObject, which was initialized with '123'.
So here's how I would do what you want:
Make your TaskManager a singleton if you want (although a static class seems sufficient), but do not serialize the singleton. Instead, store your state (all your tasks, etc) in another TaskList object that is a static and immutable field of your TaskManager, that is deserialized inside a static constructor or threadsafe singleton initialization. Don't expose the TaskList reference directly - let methods on your TaskManager class control access to it - AddTask, DeleteTask, GetTaskStatus, etc.
Your data's unique, but it's taken care of in a somewhat nicer, easier to implement manner.
I have a class with a couple of members I want to serialise to store state.
However, I want to serialise from WITHIN the class itself, not via some external class feeding it to a formatter.
So in theory I want to do something like:
[DataContract]
class MyClass
{
[DataMember]
private MyCompoundClass _someCompoundField;
[DataMember]
private int _someOtherField;
private void SaveState()
{
using (Stream stream = GetStream())
{
DataContractSerializer serialiser = new DataContractSerializer(typeof(MyClass));
serialiser.WriteObject(stream, this);
}
}
private void LoadState()
{
using (Stream stream = GetStream())
{
DataContractSerializer serialiser = new DataContractSerializer(typeof(MyClass));
this = (MyClass)serialiser.ReadObject(stream);
}
}
}
Now obviously the line
this = (MyClass)serialiser.ReadObject(stream);
is nonsense, but you can see what I'm trying to do. I want to serialise the two fields of my class from within the class. (I am using the WCF serializer, but I assume this will be the same if I use XmlSerializer).
I tried to implement this properly by serialising each field myself like so:
private void SaveState()
{
using (Stream stream = GetStream())
{
//serialise field 1
DataContractSerializer serialiser = new DataContractSerializer(typeof(MyCompoundClass));
serialiser.WriteObject(stream, _someCompoundField);
//serialise field 2
serialiser = new DataContractSerializer(typeof(int));
serialiser.WriteObject(stream, _someOtherField);
}
}
Now this works as a save, but when I come to read the document back in it throws an exception since there are two root nodes in the XML file.
How do I create my "wrapper" node to wrap my fields. Or is there some other way I should be doing this?
Many thanks,
I haven't seen any built-in deserialisation methods which modify an existing object, rather than returning a new one. Your options as I see them are:
Deserialise a new MyClass and copy the members over
Make LoadState static and have it return the deserialised MyClass
Use a different serialisation mechanism which can do what you want
you could at your LoadState do:
private void LoadState()
{
using (Stream stream = GetStream())
{
DataContractSerializer serialiser = new DataContractSerializer(typeof(MyClass));
MyClass deserialized = (MyClass)serialiser.ReadObject(stream);
this._someCompoundField = deserialized._someCompoundField;
this._someOtherField = deserialized._someOtherField;
}
}
Are you tied to a specific serializer? protobuf-net supports that use-case, for example:
[DataContract]
class MyClass
{
[DataMember(Order = 1)]
private MyCompoundClass _someCompoundField;
[DataMember(Order = 2)]
private int _someOtherField;
private void SaveState()
{
using (Stream stream = GetStream())
{
ProtoBuf.Serializer.Serialize(stream, this);
}
}
private void LoadState()
{
using (Stream stream = GetStream())
{
ProtoBuf.Serializer.Merge(stream, this);
}
}
}
Note the addition of Order = n on the member-attributes; that is because protobuf uses numeric identifiers on fields/properties, and needs a way to choose them. You can also use the project-specific [ProtoContract]/[ProtoMember(n)] attributes, but it works with the WCF ones too (as shown).
(Merge is also available on the non-generic 2.0 API - but you pass this in as an argument to Deseroalize instead)
Put your deserialization method in a static method:
class MyClass
{
public static MyClass LoadState()
{
// Deserialize, and return the new MyClass instance.
}
}
To serialize just some objects, annotate some fields to avoid serialization, or create a superclass or interface with just the fields in it you want to serialize.