How to add an attribute to an object at runtime in C#? - c#

I need to serialize any type of object. In order to serialize an object, it must have a Serializable attribute. So I need to add that attribute to any object at runtime. I think it's only possible by creating a new type using System.Reflection.
Here's a simple method that tests whether the object is serializable (has the Serializable attribute) or not.
bool IsObjectSerializable(object obj)
{
try
{
BinaryFormatter formatter = new BinaryFormatter();
MemoryStream stream = new MemoryStream();
// If it's not serializable (aka doesn't have the Serializable attribute) it will thrown an exception.
formatter.Serialize(stream, obj);
return true;
}
catch(SerializationException)
{
return false;
}
}
I have a serializable and a non-serializable class:
[Serializable]
class SerializableTestObject
{
}
class TestObject
{
}
void Main()
{
Console.WriteLine(IsObjectSerializable(new SerializableTestObject())); // prints true
Console.WriteLine(IsObjectSerializable(new TestObject())); // prints false
dynamic modifiedTestObject = AddAttributeToObject<SerializableAttribute>(new TestObject());
Console.WriteLine(IsObjectSerializable(modifiedTestObject)); // should print true
}
What do I need to write in AddAttributeToObject?
I've done some research, but I found very little resources on this topic, and most people just say it's hard to do and stuff like that.

Related

Binary serialization without serializable attribute

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);

Why this deserialized throws me StackOverflow exception?

When I try to deserialized my FooContainer (List) throws me an error. Please, just watch the last part of this code.
public interface IFoo
{
object Value { get; }
}
public abstract class Foo<T> : IFoo
{
[XmlElement("Value")]
public T Value { get; set; }
[XmlIgnore]
object IFoo.Value { get { return Value; } }
}
public class FooA : Foo<string> { }
public class FooB : Foo<int> { }
public class FooC : Foo<List<Double>> { }
[XmlRoot("Foos")]
public class FooContainer : List<IFoo>, IXmlSerializable
{
public XmlSchema GetSchema()
{
throw new NotImplementedException();
}
public void ReadXml(XmlReader reader)
{
XmlSerializer serial = new XmlSerializer(typeof(FooContainer));
serial.Deserialize(reader);
}
public void WriteXml(XmlWriter writer)
{
ForEach(x =>
{
XmlSerializer serial = new XmlSerializer(x.GetType(),
new Type[] { typeof(FooA), typeof(FooB), typeof(FooC) });
serial.Serialize(writer, x);
});
}
}
class Program
{
static void Main(string[] args)
{
FooContainer fooList = new FooContainer()
{
new FooA() { Value = "String" },
new FooB() { Value = 2 },
new FooC() { Value = new List<double>() {2, 3.4 } }
};
XmlSerializer serializer = new XmlSerializer(fooList.GetType(),
new Type[] { typeof(FooA), typeof(FooB), typeof(FooC) });
System.IO.TextWriter textWriter = new System.IO.StreamWriter(#"C:\temp\movie.xml");
serializer.Serialize(textWriter, fooList);
textWriter.Close();
XmlReader reader = XmlReader.Create(#"C:\temp\movie.xml");
var a = serializer.Deserialize(reader);
}
}
What am I doing wrong?
I had the exact same Problem:
To me it seems that you are missing set in your interface IFoo or class Foo. Without set the object becomes readonly and you will receive a StackOverflowException when trying to deserialize XML to Object.
I simply added set to my attributes, and the thing solved itself.
You don't seem to understand what IXmlSerializable is for. It's meant for overriding the default behaviour of an XmlSerializer. If you just spin up another XmlSerializer to actually serialize/deserialize in your WriteXml/ReadXml methods then yes, you will get a StackOverflowException because that serializer is going to call the exact same WriteXml/ReadXml/ method it came from on exactly the same object.
Just get rid of that IXmlSerializable implementation, since it doesn't actually do anything. Or read this CP article:
How to Implement IXmlSerializable Correctly
Basically, you are supposed to use the standard Read* and Write* methods on XmlReader or XmlWriter to read or write the object.
It's not necessarily always wrong to use an XmlSerializer inside a ReadXml or WriteXml method - as long as you're using it to serialize/deserialize a different object, one which neither equals nor contains an instance of the declaring type in the object graph.
I'd debug it to make sure, but my guess is your implementation of ReadXml is recursive. ReadXml calls Deserialize, which calls ReadXml, which calls Deserialize, etc.
ReadXml should read the xml using the reader that's passed in and hydrate the object (i.e. this) based on the Xml data.
from MSDN
The ReadXml method must reconstitute your object using the information
that was written by the WriteXml method.
When this method is called, the reader is positioned at the start of
the element that wraps the information for your type. That is, just
before the start tag that indicates the beginning of a serialized
object. When this method returns, it must have read the entire element
from beginning to end, including all of its contents. Unlike the
WriteXml method, the framework does not handle the wrapper element
automatically. Your implementation must do so. Failing to observe
these positioning rules may cause code to generate unexpected runtime
exceptions or corrupt data.
When implementing this method, you should consider the possibility
that a malicious user might provide a well-formed but invalid XML
representation in order to disable or otherwise alter the behavior of
your application.

How to do singleton serialization in C#?

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.

How to serialize multiple members of "this"

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.

C# Custom Serialization - Using TypeConverter

So I need to serialize a generic dictionary like Dictionary<long, List<MyClass>>. I need to serialize it to store it in the ViewState of an ASP.Net application.
I found an example of using TypeConverter to convert my class to a string to be serialized, but I get an error message saying MyClass is not marked as serializable.
Here is the code for my class..
[TypeConverter (typeof(MyClass_Converter))]
public class MyClass
{
// some properties
}
public class MyClass_Converter : System.ComponentModel.TypeConverter
{
public override bool CanConvertTo(...)
{
// code
}
// CanConvertFrom, ConvertFrom, ConvertTo methods
}
Then when I want to serialize it, I am using this code...
LosFormatter los = new LosFormatter();
StringWriter sw = new StringWriter();
los.Serialize(sw, hiddenData);
String resultSt = sw.GetStringBuilder().ToString();
ViewState["HiddenData"] = resultSt;
Am I doing something wrong?
Add the [Serializable] attribute to your class.
http://msdn.microsoft.com/en-us/library/system.serializableattribute.aspx

Categories