I need to make all my entities serializable. So I was thinking in a BaseEntity with a Backup and a Restore method. But in the restore I can't override the object with the saved one because this is read-only.
Any solution or some other way to get the serializable entities?
My code:
internal class BaseEntity
{
private MemoryStream ms = new MemoryStream();
private BinaryFormatter bf = new BinaryFormatter();
public void Backup()
{
bf.Serialize(ms, this);
}
public void Restore()
{
this = (BaseEntity)bf.Deserialize(ms);
}
}
The more common pattern is to not make it the responsibility of your objects to serialize/deserialize themselves; rather, use an external serializer:
var serializer = new DataContractJsonSerializer(typeof(YourClass));
var stream = ...;
YourClass yourObj = ...;
serializer.WriteObject(stream, yourObj);
var restoredObj = serializer.ReadObject(stream);
Edit: One way serialization can work is to use the System.Runtime.Serialization.Formatters.Binary.BinaryFormatter (or other implementation of IFormatter). To serialize an object you pass the object and a stream. To Deserialize the object, you pass a stream (positioned at the begining of your serialized data), and it returns the serialized object and all its depenedencies.
public static class EntityBackupServices
{
public static MemoryStream Backup (BaseEntity entity)
{
var ms = new MemoryStream();
Serialize (ms, entity);
ms.Position = 0;
return ms;
}
public static void Serialize (Stream stream, BaseEntity entity)
{
var binaryFormatter = new BinaryFormatter();
binaryFormatter.Serialize (stream, entity);
}
public static BaseEntity Restore (Stream stream)
{
var binaryFormatter = new BinaryFormatter();
var entity = (BaseEntity) binaryFormatter.Deserialize (stream);
return entity;
}
}
One thing a formatter don't do (though the FormatterServices class makes it possible) is modify existing objects. So you probably don't want to have an instance method called Deserialize. You can't really do this: new LionEntity().Deserialize () where it replaces the fields of an existing instance.
Note: You'll need to put Serializable over all your types. Any fields that can't be serialized (because it's either not a struct, or it's not marked as [Serializable] will need to be marked with NonSerialized.
// A test object that needs to be serialized.
[Serializable()]
public class BaseEntity
{
public int member1;
public string member2;
public string member3;
public double member4;
// A field that is not serialized.
[NonSerialized()] public MyRuntimeType memberThatIsNotSerializable;
public TestSimpleObject()
{
member1 = 11;
member2 = "hello";
member3 = "hello";
member4 = 3.14159265;
memberThatIsNotSerializable = new Form ();
}
public MemoryStream Backup ()
{
return EntityBackupServices.Backup (this);
}
}
Edit:
The way I've mentioned is a rather standard and accepted way. If you want to venture into hackdom, you can deserialize the object the way I've mentioned, then use reflection to set each field on your existing object to the value of the deserialized object.
public class BaseEntity
{
void Restore(Stream stream)
{
object deserialized = EntityBackupServices.RestoreDeserialize(stream);//As listed above
if (deserialized.GetType () != this.GetType ())
throw new Exception();
foreach (FieldInfo fi in GetType().GetFields())
{
fi.SetValue(this, fi.GetValue (deserialized));
}
}
}
public IEntidadBase Restore()
{
return (IEntidadBase)bf.Deserialize(ms);
}
#jacklondon how would you do EntitySerializer methods?
You can do serialization process with http://www.servicestack.net/ StackService.Text module for clean entities. You don't need any attribute (serializable/datacontract) in ms way.
public class EntityFoo
{
public string Bar { get; set; }
public EntityFoo (string bar)
{
Bar = bar;
}
}
public class EntityDumper //and the EntitySerializer
{
public static string Dump<T> (T entity)
{
return new TypeSerializer<T> ().SerializeToString (entity);
}
public static T LoadBack<T> (string dump)
{
return new TypeSerializer<T> ().DeserializeFromString (dump);
}
}
public class dump_usage
{
public void start ()
{
string dump = EntityDumper.Dump (new EntityFoo ("Space"));
EntityFoo loaded = EntityDumper.LoadBack<EntityFoo> (dump);
Debug.Assert (loaded.Bar == "Space");
}
}
I don't necessarily recommend this, but here is one pattern for an object that can persist and restore its own state using serialization that creates new instances:
public sealed class MyClass
{
private Data _data = new Data();
//Properties go here (access the public fields on _data)
public void Backup()
{
//Serialize Data
}
public void Restore()
{
//Deserialize Data and set new instance
}
private sealed class Data
{
//Public fields go here (they're private externally [because Data is private], but public to MyClass.)
}
}
Note that this only works if your serializer supports non-public classes. Worst-case, you have to make the nested class public, which is ugly, but doesn't hurt encapsulation (since the instance is private).
Related
i want to serialize & deserialize a object (this object has reference) using BinaryFormatter.
i have expected that 'DeserializedObject.Equals(A.Empty)' is same to below code.
but, a result is different.
in order to 'DeserializedObject == A.Empty', how to use serialize/deserialize ?
[Serializable]
public class A
{
private string ID = null;
private string Name = null;
public A()
{ }
public static A Empty = new A()
{
ID = "Empty",
Name = "Empty"
};
}
class Program
{
static void Main(string[] args)
{
A refObject = A.Empty; // Add reference with static object(Empty)
A DeserializedObject;
//Test
//before serialization, refObject and A.Empty is Same!!
if(refObject.Equals(A.Empty))
{
Console.WriteLine("refObject and A.Empty is the same ");
}
//serialization
using (Stream stream = File.Create("C:\\Users\\admin\\Desktop\\test.mbf"))
{
BinaryFormatter bin = new BinaryFormatter();
bin.Serialize(stream, refObject);
}
//Deserialization
using (Stream stream = File.Open("C:\\Users\\admin\\Desktop\\test.mbf", FileMode.Open))
{
BinaryFormatter bin = new BinaryFormatter();
DeserializedObject = (A)bin.Deserialize(stream);
}
//compare DeserializedObject and A.Empty again.
//After deserialization, DeserializedObject and A.Empty is Different!!
if (DeserializedObject.Equals(A.Empty))
{
Console.WriteLine("Same");
}
else
Console.WriteLine("Different");
}
}
The reason for this is that they are different objects! You can check this by printing their GetHashCode(). The reason for this is that in your code:
refObject is a reference to A.Empty (and thus the same object)
DeserialisedObject is NOT a copy; it is a new instance and so a
different object
However DeserializedObject should contain the same values (ID and Name). Note that refObject.ID will be the same object as A.Empty.ID; DeserialisedObject.ID will not, although is should contain (a copy of) the same data.
If you're just testing that deserialization is working, test that the values contained by DeserializedObject and A.Empty are the same.
If you have an immutable type which has one or more global singletons that represent standard values of the type (A.Empty in your case), you can implement the IObjectReference interface and make BinaryFormatter replace deserialized instances of the type with the appropriate, equivalent global singleton. Thus:
[Serializable]
public class A : IObjectReference
{
private string ID = null;
private string Name = null;
public A()
{ }
public static A Empty = new A()
{
ID = "Empty",
Name = "Empty"
};
#region IObjectReference Members
object IObjectReference.GetRealObject(StreamingContext context)
{
if (this.GetType() == Empty.GetType() // Type check because A is not sealed
&& this.ID == Empty.ID
&& this.Name == Empty.Name)
return Empty;
return this;
}
#endregion
}
And, to test:
public class TestClass
{
public static void Test()
{
A refObject = A.Empty; // Add reference with static object(Empty)
Test(refObject, true);
A dummy = new A(); // No global singleton for this one.
Test(dummy, false);
}
private static void Test(A refObject, bool shouldBeEqual)
{
Console.WriteLine(string.Format("refObject and A.Empty are {0}.", object.ReferenceEquals(refObject, A.Empty) ? "identical" : "different"));
var binary = BinaryFormatterHelper.ToBase64String(refObject);
var DeserializedObject = BinaryFormatterHelper.FromBase64String<A>(binary);
Console.WriteLine(string.Format("DeserializedObject and A.Empty are {0}.", object.ReferenceEquals(DeserializedObject, A.Empty) ? "identical" : "different"));
Debug.Assert(object.ReferenceEquals(refObject, A.Empty) == object.ReferenceEquals(DeserializedObject, A.Empty)); // No assert
Debug.Assert(shouldBeEqual == object.ReferenceEquals(refObject, DeserializedObject)); // No assert
}
}
public static class BinaryFormatterHelper
{
public static string ToBase64String<T>(T obj)
{
using (var stream = new MemoryStream())
{
new BinaryFormatter().Serialize(stream, obj);
return Convert.ToBase64String(stream.GetBuffer(), 0, checked((int)stream.Length)); // Throw an exception on overflow.
}
}
public static T FromBase64String<T>(string data)
{
return FromBase64String<T>(data, null);
}
public static T FromBase64String<T>(string data, BinaryFormatter formatter)
{
using (var stream = new MemoryStream(Convert.FromBase64String(data)))
{
formatter = (formatter ?? new BinaryFormatter());
var obj = formatter.Deserialize(stream);
if (obj is T)
return (T)obj;
return default(T);
}
}
}
Okay, I'm a little fuzzy on how this works or if it's possible. I want to serialize the Child class, but I don't actually want to serialize a Parent object when it does the Child.MyParent field... I just want the reference to be serialized. Is this possible and how would I go about it?
public class Parent
{
public Child New()
{
return new Child(this);
}
}
public class Child
{
public Parent MyParent;
public Child(Parent parent)
{
MyParent = parent;
}
}
Edit: I'm using DataContractSerializer, but I'm not opposed to switching to something else if necessary.
The XMLIgnoreAttribute can be applied to fields that you don's want serialized. For example,
public class Child
{
[XmlIgnore]
public Parent MyParent;
public Child(Parent parent)
{
MyParent = parent;
}
}
But as far as serializing a reference to the field, you'd have to provide more info on how you plan to persist the object that the reference points to. What is your reason to not just serialize the Parent member (in your case)? It's common to serialize all the public members that are needed.
If you just want to use serialization to clone, something like this should work:
private static Parent Clone(Parent parent)
{
Parent parentClone = null;
lock (m_lock) // serialize cloning.
{
IFormatter formatter = new BinaryFormatter();
MemoryStream stream = new MemoryStream();
using (stream)
{
formatter.Serialize(stream, parent);
stream.Seek(0, SeekOrigin.Begin);
parentClone = (Parent)formatter.Deserialize(stream);
}
}
return parentClone;
}
Sounds like you might need to implement your own serialization and deserialization functionality.
http://msdn.microsoft.com/en-us/library/system.runtime.serialization.iserializable.getobjectdata.aspx
Here's an extract from MSDN
[Serializable]
public class Person : ISerializable
{
private string name_value;
private int ID_value;
public Person() { }
protected Person(SerializationInfo info, StreamingContext context)
{
if (info == null)
throw new System.ArgumentNullException("info");
name_value = (string)info.GetValue("AltName", typeof(string));
ID_value = (int)info.GetValue("AltID", typeof(int));
}
[SecurityPermission(SecurityAction.LinkDemand,Flags = SecurityPermissionFlag.SerializationFormatter)]
public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
{
if (info == null)
throw new System.ArgumentNullException("info");
info.AddValue("AltName", "XXX");
info.AddValue("AltID", 9999);
}
public string Name
{
get { return name_value; }
set { name_value = value; }
}
public int IdNumber
{
get { return ID_value; }
set { ID_value = value; }
}
}
I'm trying to find a way to pass objects to the Azure Queue. I couldn't find a way to do this.
As I've seen I can pass string or byte array, which is not very comfortable for passing objects.
Is there anyway to pass custom objects to the Queue?
Thanks!
You can use the following classes as example:
[Serializable]
public abstract class BaseMessage
{
public byte[] ToBinary()
{
BinaryFormatter bf = new BinaryFormatter();
byte[] output = null;
using (MemoryStream ms = new MemoryStream())
{
ms.Position = 0;
bf.Serialize(ms, this);
output = ms.GetBuffer();
}
return output;
}
public static T FromMessage<T>(CloudQueueMessage m)
{
byte[] buffer = m.AsBytes;
T returnValue = default(T);
using (MemoryStream ms = new MemoryStream(buffer))
{
ms.Position = 0;
BinaryFormatter bf = new BinaryFormatter();
returnValue = (T)bf.Deserialize(ms);
}
return returnValue;
}
}
Then a StdQueue (a Queue that is strongly typed):
public class StdQueue<T> where T : BaseMessage, new()
{
protected CloudQueue queue;
public StdQueue(CloudQueue queue)
{
this.queue = queue;
}
public void AddMessage(T message)
{
CloudQueueMessage msg =
new CloudQueueMessage(message.ToBinary());
queue.AddMessage(msg);
}
public void DeleteMessage(CloudQueueMessage msg)
{
queue.DeleteMessage(msg);
}
public CloudQueueMessage GetMessage()
{
return queue.GetMessage(TimeSpan.FromSeconds(120));
}
}
Then, all you have to do is to inherit the BaseMessage:
[Serializable]
public class ParseTaskMessage : BaseMessage
{
public Guid TaskId { get; set; }
public string BlobReferenceString { get; set; }
public DateTime TimeRequested { get; set; }
}
And make a queue that works with that message:
CloudStorageAccount acc;
if (!CloudStorageAccount.TryParse(connectionString, out acc))
{
throw new ArgumentOutOfRangeException("connectionString", "Invalid connection string was introduced!");
}
CloudQueueClient clnt = acc.CreateCloudQueueClient();
CloudQueue queue = clnt.GetQueueReference(processQueue);
queue.CreateIfNotExist();
this._queue = new StdQueue<ParseTaskMessage>(queue);
Hope this helps!
Extension method that uses Newtonsoft.Json and async
public static async Task AddMessageAsJsonAsync<T>(this CloudQueue cloudQueue, T objectToAdd)
{
var messageAsJson = JsonConvert.SerializeObject(objectToAdd);
var cloudQueueMessage = new CloudQueueMessage(messageAsJson);
await cloudQueue.AddMessageAsync(cloudQueueMessage);
}
I like this generalization approach but I don't like having to put Serialize attribute on all the classes I might want to put in a message and derived them from a base (I might already have a base class too) so I used...
using System;
using System.Text;
using Microsoft.WindowsAzure.Storage.Queue;
using Newtonsoft.Json;
namespace Example.Queue
{
public static class CloudQueueMessageExtensions
{
public static CloudQueueMessage Serialize(Object o)
{
var stringBuilder = new StringBuilder();
stringBuilder.Append(o.GetType().FullName);
stringBuilder.Append(':');
stringBuilder.Append(JsonConvert.SerializeObject(o));
return new CloudQueueMessage(stringBuilder.ToString());
}
public static T Deserialize<T>(this CloudQueueMessage m)
{
int indexOf = m.AsString.IndexOf(':');
if (indexOf <= 0)
throw new Exception(string.Format("Cannot deserialize into object of type {0}",
typeof (T).FullName));
string typeName = m.AsString.Substring(0, indexOf);
string json = m.AsString.Substring(indexOf + 1);
if (typeName != typeof (T).FullName)
{
throw new Exception(string.Format("Cannot deserialize object of type {0} into one of type {1}",
typeName,
typeof (T).FullName));
}
return JsonConvert.DeserializeObject<T>(json);
}
}
}
e.g.
var myobject = new MyObject();
_queue.AddMessage( CloudQueueMessageExtensions.Serialize(myobject));
var myobject = _queue.GetMessage().Deserialize<MyObject>();
In case the storage queue is used with WebJob or Azure function (quite common scenario) then the current Azure SDK allows to use POCO object directly. See examples here:
https://learn.microsoft.com/en-us/sandbox/functions-recipes/queue-storage
https://github.com/Azure/azure-webjobs-sdk/wiki/Queues#trigger
Note: The SDK will automatically use Newtonsoft.Json for serialization/deserialization under the hood.
I liked #Akodo_Shado's approach to serialize with Newtonsoft.Json. I updated it for Azure.Storage.Queues and also added a "Retrieve and Delete" method that deserializes the object from the queue.
public static class CloudQueueExtensions
{
public static async Task AddMessageAsJsonAsync<T>(this QueueClient queueClient, T objectToAdd) where T : class
{
string messageAsJson = JsonConvert.SerializeObject(objectToAdd);
BinaryData cloudQueueMessage = new BinaryData(messageAsJson);
await queueClient.SendMessageAsync(cloudQueueMessage);
}
public static async Task<T> RetreiveAndDeleteMessageAsObjectAsync<T>(this QueueClient queueClient) where T : class
{
QueueMessage[] retrievedMessage = await queueClient.ReceiveMessagesAsync(1);
if (retrievedMessage.Length == 0) return null;
string theMessage = retrievedMessage[0].MessageText;
T instanceOfT = JsonConvert.DeserializeObject<T>(theMessage);
await queueClient.DeleteMessageAsync(retrievedMessage[0].MessageId, retrievedMessage[0].PopReceipt);
return instanceOfT;
}
}
The RetreiveAndDeleteMessageAsObjectAsync is designed to process 1 message at time, but you could obviously rewrite to deserialize the full array of messages and return a ICollection<T> or similar.
That is not right way to do it. queues are not ment for storing object. you need to put object in blob or table (serialized).
I believe queue messgae body has 64kb size limit with sdk1.5 and 8kb wih lower versions.
Messgae body is ment to transfer crutial data for workera that pick it up only.
I'm trying out Generics and I had this (not so) great idea of creating an XMLSerializer class. The code I pieced together is below:
public class Persist<T>
{
private string _path;
public Persist(string path) {
this._path = path;
}
public void save(T objectToSave)
{
XmlSerializer s = new XmlSerializer(typeof(T));
TextWriter w = new StreamWriter(this._path);
try { s.Serialize(w, objectToSave); }
catch (InvalidDataException e) { throw e; }
w.Close(); w.Dispose();
}
public T load()
{
XmlSerializer s = new XmlSerializer(typeof(T));
TextReader r = new StreamReader(this._path);
T obj;
try { obj = (T)s.Deserialize(r); }
catch (InvalidDataException e) { throw e; }
r.Close(); r.Dispose();
return obj;
}
}
Here's the problem: It works fine on Persist<List<string>> or Persist<List<int>> but not on Persist<List<userObject>> or any other custom (but serializable) objects. userObject itself is just a class with two {get;set;} properties, which I have serialized before.
I'm not sure if the problems on my Persist class (generics), XML Serialization code, or somewhere else :( Help is very much appreciated~
Edit:
code for userObject
public class userObject
{
public userObject(string id, string name)
{
this.id = id;
this.name = name;
}
public string id { get;private set; }
public string name { get;set; }
}
Looks to me like your code should just work - even though it does have a few flaws.
EDIT: Your userObject class isn't serializable. Xml serialization only works on types with a public, parameterless constructor - the current class won't work. Also, you should really rewrite your code to avoid explicit calls to .Close() or .Dispose() and instead prefer using where possible - as is, you might get random file locking if at any point during serialization an error occurs and your method terminates by exception - and thus doesn't call .Dispose().
Personally, I tend to use a just-for-serialization object hierarchy that's just a container for data stored in xml and avoids any behavior - particularly side effects. Then you can use a handly little base class that makes this simple.
What I use in my projects is the following:
public class XmlSerializableBase<T> where T : XmlSerializableBase<T>
{
static XmlSerializer serializer = new XmlSerializer(typeof(T));
public static T Deserialize(XmlReader from) { return (T)serializer.Deserialize(from); }
public void SerializeTo(Stream s) { serializer.Serialize(s, this); }
public void SerializeTo(TextWriter w) { serializer.Serialize(w, this); }
public void SerializeTo(XmlWriter xw) { serializer.Serialize(xw, this); }
}
...which caches the serializer in a static object, and simplifies usage (no generic type-paramenters needed at call-locations.
Real-life classes using it:
public class ArtistTopTracks {
public string name;
public string mbid;//always empty
public long reach;
public string url;
}
[XmlRoot("mostknowntracks")]
public class ApiArtistTopTracks : XmlSerializableBase<ApiArtistTopTracks> {
[XmlAttribute]
public string artist;
[XmlElement("track")]
public ArtistTopTracks[] track;
}
Sample serialization calls:
using (var xmlReader = XmlReader.Create([...]))
return ApiArtistTopTracks.Deserialize(xmlReader);
//[...]
ApiArtistTopTracks toptracks = [...];
toptracks.SerializeTo(Console.Out);
There can be a number of reasons why your code fails: This text is particularly helpful when having issues: Troubleshooting Common Problems with the XmlSerializer . Maybe you have some type hierarchy in your user objects and the serializer does not know about it?
I want to fill items in a combobox, each of them has different behaviour.
Yes I know I could simply create 3 classes deriving from a base class. But my question is kind of "is there another way" and "what is possible".
In Java one can do "new MyClass(){public void overriddenmethod(){...} }" but in C# we can not, can we?
Now I use a lambda to define a method on the fly but the problem is that I later want the new XxxFormatter() as instance variable of that object. Since the XxxFormatters share no common base class I cannot put them as a single field in the SerializingHelper class.
Do you have any Ideas?
public delegate void SerializingHandler(Stream s, object o);
class SerializingHelper
{
public string Name { get; set; }
public SerializingHandler Serializer { get; set; }
}
comboFormat.Items.AddRange(new object[]
{
new SerializingHelper{ Name = "Binary",
Serializer = (s,o)=>new BinaryFormatter().Serialize(s,o),
new SerializingHelper{ Name = "Soap",
Serializer = (s,o)=>new SoapFormatter().Serialize(s,o),
new SerializingHelper{ Name = "Xml",
Serializer = (s,o)=>new XmlSerializer(typeof(KontaktpartnerData), new Type[]
{typeof(ArrayList), typeof(KontaktPartner)}).Serialize(s,o), }
});
If you just want to prevent instantiating a new serializer instance each time, you can instantiate them outside of the lambda:
var binaryFormatter = new BinaryFormatter();
comboFormat.Items.AddRange(new object[]
{
new SerializingHelper
{
Name = "Binary",
Serializer = binaryFormatter.Serialize
}
...
});
If you really need to store the formatter as a field, you could do something like this:
delegate void SerializingHandler<TFormatter>(TFormatter formatter,
Stream stream,
object graph);
interface ISerializingHelper
{
void Serialize(Stream stream, object graph);
}
class SerializingHelper<TFormatter> : ISerializingHelper
{
private readonly SerializingHandler<TFormatter> handler;
private readonly TFormatter formatter;
public SerializingHelper(SerializingHandler<TFormatter> handler,
TFormatter formatter)
{
this.handler = handler;
this.formatter = formatter;
}
public TFormatter Formatter
{
get { return this.formatter; }
}
public void Serialize(Stream stream, object graph)
{
this.handler(this.formatter, stream, graph);
}
}