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.
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 want to serailize my object and used BinaryFormatter class.
public static byte[] BinarySerialize(IMessage message)
{
using (var stream = new MemoryStream())
{
var formatter = new BinaryFormatter();
formatter.Serialize(stream, message);
return stream.ToArray();
}
}
But when I run the code, throws an exception.
SerializationException: Object is not marked as serializable.
I think this exception thrown by BinaryFormatter.
I do not want to mark as [Serializable] my objects. Or my library users may forget mark as [Serializable] their own Messages.
Is there any other way to binary serialize my objects without using [Serializable] attribute?
Since [Serializable] attribute cannot be added runtime, there are nooptions if you want to stick to the .Net built in Serialization.
You can
Use ISerializable interface in IMessage so that users has to implement Serialization in their implementations
Use an external library such as: http://sharpserializer.codeplex.com/ And by the way, they have moved to GitHub. See: https://github.com/polenter/SharpSerializer
public static byte[] BinarySerialize(IMessage message)
{
using (var stream = new MemoryStream())
{
var serializer = new SharpSerializer(true);
serializer.Serialize(message, stream );
return stream.ToArray();
}
}
Use JSON serialization
In addition to the other answers regarding 3rd party libs, depending on your needs you may choose to use XmlSerializer. (Better yet use a JSON serializer that doesn't require the SerializeableAttribute.)
These serializers do not require [Serializeable]. However, the XmlSerializer doesn't allow serialization of interfaces either. If you are good with concrete types it works. Compare serialization options.
E.G.
void Main()
{
var serialized = Test.BinarySerialize(new SomeImpl(11,"Hello Wurld"));
}
public class Test
{
public static string BinarySerialize(SomeImpl message)
{
using (var stream = new StringWriter())
{
var formatter = new XmlSerializer(typeof(SomeImpl));
formatter.Serialize(stream, message);
return stream.ToString().Dump();
}
}
}
public class SomeImpl
{
public int MyProperty { get;set;}
public string MyString { get;set; }
public SomeImpl() {}
public SomeImpl(int myProperty, String myString)
{
MyProperty = myProperty;
MyString = myString;
}
}
To avoid Net4x built in Serialization that require the [Serializable] attribute, use Newtonsoft.Json or System.Text.Json in netcore 3.1+ or Net5
string json= JsonConvert.SerializeObject(message);
//or System.Text.Json in netcore 3.1+
string json= System.Text.Json. JsonSerializer.Serialize(message);
I faced with strange problem. After reading a lot, I tried to deploy protobuf-net in my app. Classes binds with simple inheritance. After presumably successful serialization, I am trying to deserialize to list, but protobuf filling only base class!
here a bit of code:
serializing function
private static void serialize<T>(T obj) where T: Log
{
using (var fileStream =
new FileStream(fileName, FileMode.Append))
{
Serializer.SerializeWithLengthPrefix(fileStream, obj,
PrefixStyle.Base128, SerializeTypesDictionary.First(x =>
x.Value == obj.GetType()).Key);
}
}
deserialize
private static ArrayList deserialize(string filename)
{
object obj;
var arr = new ArrayList();
using (var fileStream = new FileStream(file, FileMode.Open))
{
while (Serializer.NonGeneric.TryDeserializeWithLengthPrefix
(fileStream, PrefixStyle.Base128, resolver, out obj))
{
arr.Add(obj);
}
}
return arr;
}
and classes
[ProtoContract(ImplicitFields = ImplicitFields.AllPublic)
[ProtoInclude(100, typeof(Error))]
[ProtoInclude(101, typeof(Record))]
[ProtoInclude(102, typeof(SqlQuery))]
public class Log
{...}
[ProtoContract(ImplicitFields = ImplicitFields.AllPublic)]
public class Error:Log
{...}
and other with same structure
So, what am I doing wrong?
P.S. after deserializing list consists right classes (not base! all classes deserialised in their types), but with empty fields, and full fields in base class.
The question closed.
Sorry for wasting yours time, if you wasted it=) Simply forgot attributes on one of the derived classes, and somehow its affect to others.
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 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.