C# DataContract Serialization, how to deserialize to already existing instance - c#

I have a class, which holds a static dictionary of all existing instances, which are defined at compile time.
Basically it looks like this:
[DataContract]
class Foo
{
private static Dictionary<long, Foo> instances = new Dictionary<long, Foo>();
[DataMember]
private long id;
public static readonly Foo A = Create(1);
public static readonly Foo B = Create(2);
public static readonly Foo C = Create(3);
private static Foo Create(long id)
{
Foo instance = new Foo();
instance.id = id;
instances.Add(instance);
return instance;
}
public static Foo Get(long id)
{
return instances[id];
}
}
There are other fields, and the class is derived, but this doesn't matter for the problem.
Only the id is serialized. When an instance of this type is deserialized, I would like to get the instance that has been created as the static field (A, B or C), using Foo.Get(id) instead of getting a new instance.
Is there a simple way to do this? I didn't find any resources which I was able to understand.

During deserialization it (AFAIK) always uses a new object (FormatterServices.GetUninitializedObject), but to get it to substitute the objects after deserialization (but before they are returned to the caller), you can implement IObjectReference, like so:
[DataContract]
class Foo : IObjectReference { // <===== implement an extra interface
object IObjectReference.GetRealObject(StreamingContext ctx) {
return Get(id);
}
...snip
}
done... proof:
static class Program {
static void Main() {
Foo foo = Foo.Get(2), clone;
DataContractSerializer ser = new DataContractSerializer(typeof(Foo));
using (MemoryStream ms = new MemoryStream()) { // clone it via DCS
ser.WriteObject(ms, foo);
ms.Position = 0;
clone = (Foo)ser.ReadObject(ms);
}
Console.WriteLine(ReferenceEquals(foo, clone)); // true
}
}
Note there are some extra notes on this for partial trust scenarios on MSDN, here.

I had a similar problem and the best solution that I found was adding some wrapper class, that was managing instances of the one needed to be serialized.
I am not sure about the exact signature with Contracts. I used SerializableAttribute, and with it i looked smth. like that:
[Serializable]
class FooSerializableWrapper : ISerializable
{
private readonly long id;
public Foo Foo
{
get
{
return Foo.Get(id);
}
}
public FooSerializableWrapper(Foo foo)
{
id = foo.id;
}
protected FooSerializableWrapper(SerializationInfo info, StreamingContext context)
{
id = info.GetInt64("id");
}
void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("id", id);
}
}

You may be able to get a step towards what you are looking for using OnDeserializingAttribute. However, that will probably only let you set properties (so you could have what amounts to a Copy method that populates all the properties of the current instance using your static instance.
I think if you actually want to return your static instances, you'd probably have to write your own Deserializer...
Untested, but I would assume you could implement a deserializer pretty easily like this:
public class MyDeserializer : System.Xml.Serialization.XmlSerializer
{
protected override object Deserialize(System.Xml.Serialization.XmlSerializationReader reader)
{
Foo obj = (Foo)base.Deserialize(reader);
return Foo.Get(obj.id);
}
}
Note that you'll have to do something about getting the ID since it is private in your code; Also this assumes you are using XML serialization; Replace the inheritance with whatever you actually are using. And finally, this means you'll have to instantiate this type when deserializing your objects, which may involve changing some code and/or configuration.

No problem, just use 2 classes. In getObject method you get your existing object
[Serializable]
public class McRealObjectHelper : IObjectReference, ISerializable
{
Object m_realObject;
virtual object getObject(McObjectId id)
{
return id.GetObject();
}
public McRealObjectHelper(SerializationInfo info, StreamingContext context)
{
McObjectId id = (McObjectId)info.GetValue("ID", typeof(McObjectId));
m_realObject = getObject(id);
if(m_realObject == null)
return;
Type t = m_realObject.GetType();
MemberInfo[] members = FormatterServices.GetSerializableMembers(t, context);
List<MemberInfo> deserializeMembers = new List<MemberInfo>(members.Length);
List<object> data = new List<object>(members.Length);
foreach(MemberInfo mi in members)
{
Type dataType = null;
if(mi.MemberType == MemberTypes.Field)
{
FieldInfo fi = mi as FieldInfo;
dataType = fi.FieldType;
} else if(mi.MemberType == MemberTypes.Property){
PropertyInfo pi = mi as PropertyInfo;
dataType = pi.PropertyType;
}
try
{
if(dataType != null){
data.Add(info.GetValue(mi.Name, dataType));
deserializeMembers.Add(mi);
}
}
catch (SerializationException)
{
//some fiels are missing, new version, skip this fields
}
}
FormatterServices.PopulateObjectMembers(m_realObject, deserializeMembers.ToArray(), data.ToArray());
}
public object GetRealObject( StreamingContext context )
{
return m_realObject;
}
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
}
}
public class McRealObjectBinder: SerializationBinder
{
String assemVer;
String typeVer;
public McRealObjectBinder(String asmName, String typeName)
{
assemVer = asmName;
typeVer = typeName;
}
public override Type BindToType( String assemblyName, String typeName )
{
Type typeToDeserialize = null;
if ( assemblyName.Equals( assemVer ) && typeName.Equals( typeVer ) )
{
return typeof(McRealObjectHelper);
}
typeToDeserialize = Type.GetType( String.Format( "{0}, {1}", typeName, assemblyName ) );
return typeToDeserialize;
}
}
Then, when deserialize:
BinaryFormatter bf = new BinaryFormatter(null, context);
bf.Binder = new McRealObjectBinder(YourType.Assembly.FullName, YourType.FullName);
bf.Deserialize(memStream);

Related

Cast to Type with reflection without generic argument

I have an issue which involves casting to different Types
I have a class which is a wrapper, which contains an inner wrapper which contains T here is the structure:
public class Wrapper
{
public readonly Guid Foo;
public Wrapper(Guid foo)
{
Foo = foo;
}
}
public class Wrapper<T> : Wrapper
{
public readonly InnerWrapper<T> InnerWrapper;
public Wrapper(Guid foo, InnerWrapper<T> innerWrapper)
: base(foo)
{
InnerWrapper = innerWrapper;
}
}
public class InnerWrapper<T>
{
public readonly Guid Bar;
public readonly T Content;
public InnerWrapper(T content, Guid bar)
{
Content = content;
Bar = bar;
}
}
In another part of the code I have a List<Wrapper>. notice that is a list of wrapper not a list of wrapper<T> which means that T can be any class.
when this translates is expected to have something like
{
wrapper<Class1>,
wrapper<Class2>
}
and so on.
At some point in the code there is a "helper class" which contains the object as object type and the Type
public class Helper
{
public readonly object Obj;
public readonly Type Type;
public Helper(object obj, Type type)
{
Obj = obj;
Type = type;
}
}
And here is where my problem begins, I created a helper method to convert from List<Helper> to List<Wrapper> and it does it, but the inner object is type object instead of being the actual type.
And i have no clue of how to convert from object to the actual type without using generics, and as T can be multiples types I cannot use them.
This is the helper classs
public static class HelperExtensions
{
public static T CastTo<T>(this object o) => (T)o;
public static List<Wrapper> ToWrapper(this IEnumerable<Helper> helperList)
{
List<Wrapper> wrapperResponseList = new List<Wrapper>();
foreach (var help in helperList)
{
// doesn't work either var typedValue = Convert.ChangeType(help.Obj, help.Type);
var methodInfo = typeof(HelperExtensions).GetMethod(nameof(CastTo), BindingFlags.Static | BindingFlags.Public);
var genericArguments = new[] { help.Type };
var genericMethodInfo = methodInfo?.MakeGenericMethod(genericArguments);
var typedValue = genericMethodInfo?.Invoke(null, new[] { help.Obj });
var wrapper = typedValue.BuildWrapper(Guid.NewGuid(), Guid.NewGuid());
wrapperResponseList.Add(wrapper);
}
return wrapperResponseList;
}
//This one called individually does the work properly
public static Wrapper<T> BuildWrapper<T>(this T obj, Guid foo, Guid bar)
{
InnerWrapper<T> inner = new InnerWrapper<T>(obj, bar);
return new Wrapper<T>(foo, inner);
}
}
to show the code here I also Created a test class
public class Placeholder
{
public Guid Value { get; }
public Placeholder(Guid value)
{
Value = value;
}
}
so when I call
Placeholder placeholder = new Placeholder(Guid.NewGuid());
Helper helper1 = new Helper(placeholder, placeholder.GetType());
var result = new List<Helper> { helper1 }.ToWrapper();
It generates a List<Wrapper> but instead of wrapper<T> it is generating Wrapper<Object>
Any idea how can I cast it properly?
I also created a fiddle https://dotnetfiddle.net/pz3JDz which contains all the needed code to test.
Thanks In advance for your help.
The local variable typedValue is declared with var and because genericMethodInfo?.Invoke(...) returns the reply of the type object, the type of that local variable typedValue will be object. After that you call the generic method Wrapper<T> BuildWrapper<T>(this T obj, ...) here since the type of typedValue is object the type of T is will also be object, therefore it will return Wrapper<object>.
To solve it instead of this:
var wrapper = typedValue.BuildWrapper(Guid.NewGuid(), Guid.NewGuid());
Do this:
var buildWrapperMethodInfo = typeof(HelperExtensions).GetMethod(nameof(BuildWrapper), BindingFlags.Static | BindingFlags.Public);
var buildWrapperGenericMethodInfo = buildWrapperMethodInfo?.MakeGenericMethod(genericArguments);
var wrapper = buildWrapperGenericMethodInfo?.Invoke(null, new[] { typedValue, Guid.NewGuid(), Guid.NewGuid() });
And then add the type cast here:
wrapperResponseList.Add((Wrapper)wrapper);
Simplifying the call to directly retrieve the object from the BuildWrapper<T> is possible.
You can do the following. You can obviously reformat for error check but you get the idea.
var wrapperGeneric = typeof(HelperExtensions).GetMethod("BuildWrapper").MakeGenericMethod(new[] { help.Type}).Invoke(this,help.Obj)

Let DynamicObect's TryGetMember and TrySetMember return a class in C#

I have a class inheriting from DynamicObject
In this class I have a TryGetMember that I try to evaluate to a static class.
How can TryGetMember overriden from DynamicObject result in a static class?
TL;DR
Calling code:
dynamic sut = new ReachIn();
sut.myclass.MyInnerStaticClass.MyProperty= "fortytwo";
My DynamicObject class tries to return myclass as the MyClass instance.
internal class ReachIn : DynamicObject
{
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = MyClass; // Does not compile.
return true;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
value = MyClass // Does not compile;
return true;
}
}
and what is returned is here:
internal class MyClass
{
internal static class MyInnerStaticClass
{
public static string MyProperty { get; set; }
}
}
This example is a bit forced. It is just a very simplified example of a dynamic object making private fields, properties, methods (and (not yet) classes) visible for testing purpose. I also know one should not write tests this way but I have an esoteric reason. or because I can.
Regardless of your class design I am going to show that what you are trying to achieve is possible with dynamic types and reflection. Firstly a dynamic object is just an object that can take some string name in method like TryGetMember to perform some action. Secondly with string names and reflection you can perform any operation on your objects. So simple dynamic object implementation that will work with your example looks like this:
internal class ReachIn : DynamicObject
{
private readonly Type type;
private readonly string #namespace;
public ReachIn(Type type = null, string #namespace = null)
{
this.type = type;
this.#namespace = #namespace;
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if (type == null)
{
result = new ReachIn(Type.GetType($"{#namespace}.{binder.Name}".Trim('.')));
return true;
}
var member = type.GetMember(binder.Name).Single();
if (member.MemberType == MemberTypes.NestedType)
{
result = new ReachIn((Type)member);
}
else if (member.MemberType == MemberTypes.Property)
{
result = ((PropertyInfo)member).GetValue(null);
}
else
{
result = null;
return false;
}
return true;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
var member = type.GetMember(binder.Name).Single();
if (member.MemberType == MemberTypes.Property)
{
((PropertyInfo)member).SetValue(null, value);
return true;
}
return false;
}
}
So it will work for a sample class with public modifiers:
public class MyClass
{
public static class MyInnerStaticClass
{
public static string MyProperty { get; set; }
}
}
With this you can set your static property like:
dynamic sut = new ReachIn(#namespace: "ConsoleApp8");
sut.MyClass.MyInnerStaticClass.MyProperty = "safd";
It's tested only with your example so for other cases you would need to provide some additional implementation. Not to mention the performance of it would be very bad because of reflection.

How do I deserialize into an existing object - C#

In C#, after serializing an object to a file how would I deserialize the file back into an existing object without creating a new object?
All the examples I can find for custom serialization involve implementing a constructor that will be called upon deserialization which is exactly what I want except that I don't want the function to be a constructor.
Thanks!
Some serializers support callbacks; for example, both BinaryFormatter and DataContractSerializer (and protobuf-net, below) allow you to specify a before-serializaton callback, and since they skip the constructor, this may well be enough to initialize the object. The serializer is still creating it, though.
Most serializers are fussy about wanting to create the new object themselves, however some will allow you to deserialize into an existing object. Well, actually the only one that leaps to mind is protobuf-net (disclosure: I'm the author)...
This has 2 different features that might help here; for the root object (i.e. the outermost object in a graph) you can supply the existing object directly to either the Merge methods (in v1, also present in v2 for compatibility), or (in v2) the Deserialize methods; for example:
var obj = Serializer.Merge<YourType>(source, instance);
However, in a larger graph, you might want to supply other objects yourself (than just the root). The following is not exposed on the attribute API, but is a new feature in v2:
RuntimeTypeModel.Default[typeof(SomeType)].SetFactory(factoryMethod);
where factoryMethod can be either the name of a static method in SomeType (that returns a SomeType instance), or can be a MethodInfo to any static method anywhere. The method can additionally (optionally) take the serialization-context as a parameter if you want. This method should then be used to supply all new instances of SomeType.
Note: protobuf-net is not quite the same as BinaryFormatter; for best effect, you need to tell it how to map your members - very similar to marking things as [DataMember] for WCF/DataContractSerializer. This can be attributes, but does not need to be.
No problem, just use 2 classes. In getObject method you get your existing object
[Serializable]
public class McRealObjectHelper : IObjectReference, ISerializable
{
Object m_realObject;
virtual object getObject(McObjectId id)
{
return id.GetObject();
}
public McRealObjectHelper(SerializationInfo info, StreamingContext context)
{
McObjectId id = (McObjectId)info.GetValue("ID", typeof(McObjectId));
m_realObject = getObject(id);
if(m_realObject == null)
return;
Type t = m_realObject.GetType();
MemberInfo[] members = FormatterServices.GetSerializableMembers(t, context);
List<MemberInfo> deserializeMembers = new List<MemberInfo>(members.Length);
List<object> data = new List<object>(members.Length);
foreach(MemberInfo mi in members)
{
Type dataType = null;
if(mi.MemberType == MemberTypes.Field)
{
FieldInfo fi = mi as FieldInfo;
dataType = fi.FieldType;
} else if(mi.MemberType == MemberTypes.Property){
PropertyInfo pi = mi as PropertyInfo;
dataType = pi.PropertyType;
}
try
{
if(dataType != null){
data.Add(info.GetValue(mi.Name, dataType));
deserializeMembers.Add(mi);
}
}
catch (SerializationException)
{
//some fiels are missing, new version, skip this fields
}
}
FormatterServices.PopulateObjectMembers(m_realObject, deserializeMembers.ToArray(), data.ToArray());
}
public object GetRealObject( StreamingContext context )
{
return m_realObject;
}
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
}
}
public class McRealObjectBinder: SerializationBinder
{
String assemVer;
String typeVer;
public McRealObjectBinder(String asmName, String typeName)
{
assemVer = asmName;
typeVer = typeName;
}
public override Type BindToType( String assemblyName, String typeName )
{
Type typeToDeserialize = null;
if ( assemblyName.Equals( assemVer ) && typeName.Equals( typeVer ) )
{
return typeof(McRealObjectHelper);
}
typeToDeserialize = Type.GetType( String.Format( "{0}, {1}", typeName, assemblyName ) );
return typeToDeserialize;
}
}
Then, when deserialize:
BinaryFormatter bf = new BinaryFormatter(null, context);
bf.Binder = new McRealObjectBinder(YourType.Assembly.FullName, YourType.FullName);
bf.Deserialize(memStream);
If it's just a matter of copying a few fields, I would avoid all the trouble and take the simple route - deserialize into a new instance, then copy the appropriate fields to the existing instance. It will cost you a couple of extra copies, but you'll save a lot of time on debugging.
It's a bit unusual but it works:
[Serializable]
public class Pets
{
public int Cats { get; set; }
public int Dogs { get; set; }
}
public static class Utils
{
public static byte[] BinarySerialize(object o)
{
using (var ms = new MemoryStream())
{
IFormatter f = new BinaryFormatter();
f.Serialize(ms, o);
return ms.ToArray();
}
}
public static dynamic BinaryDeserialize(byte[] bytes, dynamic o)
{
using (var ms = new MemoryStream(bytes))
{
ms.Position = 0;
IFormatter f = new BinaryFormatter();
o = (dynamic)f.Deserialize(ms);
return o;
}
}
}
The deserialization is tricky with the dynamic which cannot be passed by reference.
And finally a no-sense test for proof:
Pets p = new Pets() { Cats = 0, Dogs = 3 };
Console.WriteLine("{0}, {1}", p.Cats, p.Dogs);
byte[] serial = Utils.BinarySerialize(p);
p.Cats = 1;
Console.WriteLine("{0}, {1}", p.Cats, p.Dogs);
p = Utils.BinaryDeserialize(serial, p);
Console.WriteLine("{0}, {1}", p.Cats, p.Dogs);
The output is the following:
0, 3
1, 3
0, 3
Replace memory strings with file stream and you will get the same results.

ISerializable-derived class, via FormatterServices.GetSerializableMembers() in c#

A derived class that implements ISerializable, whose based class does NOT, can de/serialise both members of based class and itself. In the derived class, FormatterServices.GetSerializableMembers() is used to get the based class's members, it should return both field and property based on MSDN. However, in the code below, it only return field. Any idea?
MSDN
internal static class ISerializableVersioning {
public static void Go() {
using (var stream = new MemoryStream()) {
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, new Derived());
stream.Position = 0;
Derived d = (Derived)formatter.Deserialize(stream);
Console.WriteLine(d);
}
}
[Serializable]
private class Base {
protected String m_name = "base";
protected String Name { get { return m_name; } set { m_name = value; } }
public Base() { /* Make the type instantiable*/ }
}
[Serializable]
private class Derived : Base, ISerializable {
new private String m_name = "derived";
public Derived() { /* Make the type instantiable*/ }
// If this constructor didn't exist, we'd get a SerializationException
// This constructor should be protected if this class were not sealed
[SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]
private Derived(SerializationInfo info, StreamingContext context) {
// Get the set of serializable members for our class and base classes
Type baseType = this.GetType().BaseType;
MemberInfo[] mi = FormatterServices.GetSerializableMembers(baseType, context);
// Deserialize the base class's fields from the info object
for (Int32 i = 0; i < mi.Length; i++) {
// Get the field and set it to the deserialized value
FieldInfo fi = (FieldInfo)mi[i];
fi.SetValue(this, info.GetValue(baseType.FullName + "+" + fi.Name, fi.FieldType));
}
// Deserialize the values that were serialized for this class
m_name = info.GetString("Name");
}
[SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]
public virtual void GetObjectData(SerializationInfo info, StreamingContext context) {
// Serialize the desired values for this class
info.AddValue("Name", m_name);
// Get the set of serializable members for our class and base classes
Type baseType = this.GetType().BaseType;
//**Should GetSerializableMembers return both the field and property? But it only return field here**
MemberInfo[] mi = FormatterServices.GetSerializableMembers(baseType, context);
// Serialize the base class's fields to the info object
for (Int32 i = 0; i < mi.Length; i++) {
// Prefix the field name with the fullname of the base type
object value = ((FieldInfo) mi[i]).GetValue(this);
info.AddValue(baseType.FullName + "+" + mi[i].Name, value);
}
}
public override String ToString() {
return String.Format("Base Name={0}, Derived Name={1}", base.Name, m_name);
}
}
}
GetSerializableMembers returns a MemberInfo array; you are casting them all to FieldInfo even though they could be EventInfo, MethodBase, or PropertyInfo objects.

c# - How to deserialize a generic list<T> when I don't know the type of (T)?

for auditory reasons I stores the arguments of the business methods serialized into the database using the binaryformatter.
The problem is that when an argument is a generic list I don't find the way to cast the deserialized object because I don't know the type, or If I will know the type I don't know how to cast the object at runtime.
Anybody knows how to cast an object containing a generic list dinamically at runtime?
I need to do this because I need to show the deserialized data in a property grid:
object objArg = bformatter.Deserialize(memStr);
//If the type is a clr type (int, string, etc)
if (objArg.GetType().Module.Name == "mscorlib.dll")
{
//If the type is a generic type (List<>, etc)
//(I'm only use List for these cases)
if (objArg.GetType().IsGenericType)
{
// here is the problem
pgArgsIn.SelectedObject = new { Value = objArg};
//In the previous line I need to do something like...
//new { Value = (List<objArg.GetYpe()>) objArg};
}
else
{
pgArgsIn.SelectedObject = new { Value = objArg.ToString() };
}
}
else
{
//An entity object
pgArgsIn.SelectedObject = objArg;
}
With BinaryFormatter you don't need to know the type; the metadata is included in the stream (making it bigger, but hey!). However, you can't cast unless you know the type. Often in this scenario you have to use common known interfaces (non-generic IList etc) and reflection. And lots of it.
I also can't think of a huge requirement to know the type to show in a PropertyGrid - since this accepts object, just give it what BinaryFormatter provides. Is there a specific issue you are seeing there? Again, you might want to check for IList (non-generic) - but it isn't worth worrying about IList<T>, since this isn't what PropertyGrid checks for!
You can of course find the T if you want (like so) - and use MakeGenericType() and Activator.CreateInstance - not pretty.
OK; here's a way using custom descriptors that doesn't involve knowing anything about the object or the list type; if you really want it is possible to expand the list items directly into the properties, so in this example you'd see 2 fake properties ("Fred" and "Wilma") - that is extra work, though ;-p
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
class Person
{
public string Name { get; set; }
public DateTime DateOfBirth { get; set; }
public override string ToString() {
return Name;
}
}
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Person fred = new Person();
fred.Name = "Fred";
fred.DateOfBirth = DateTime.Today.AddYears(-23);
Person wilma = new Person();
wilma.Name = "Wilma";
wilma.DateOfBirth = DateTime.Today.AddYears(-20);
ShowUnknownObject(fred, "Single object");
List<Person> list = new List<Person>();
list.Add(fred);
list.Add(wilma);
ShowUnknownObject(list, "List");
}
static void ShowUnknownObject(object obj, string caption)
{
using(Form form = new Form())
using (PropertyGrid grid = new PropertyGrid())
{
form.Text = caption;
grid.Dock = DockStyle.Fill;
form.Controls.Add(grid);
grid.SelectedObject = ListWrapper.Wrap(obj);
Application.Run(form);
}
}
}
[TypeConverter(typeof(ListWrapperConverter))]
public class ListWrapper
{
public static object Wrap(object obj)
{
IListSource ls = obj as IListSource;
if (ls != null) obj = ls.GetList(); // list expansions
IList list = obj as IList;
return list == null ? obj : new ListWrapper(list);
}
private readonly IList list;
private ListWrapper(IList list)
{
if (list == null) throw new ArgumentNullException("list");
this.list = list;
}
internal class ListWrapperConverter : TypeConverter
{
public override bool GetPropertiesSupported(ITypeDescriptorContext context)
{
return true;
}
public override PropertyDescriptorCollection GetProperties(
ITypeDescriptorContext context, object value, Attribute[] attributes) {
return new PropertyDescriptorCollection(
new PropertyDescriptor[] { new ListWrapperDescriptor(value as ListWrapper) });
}
}
internal class ListWrapperDescriptor : PropertyDescriptor {
private readonly ListWrapper wrapper;
internal ListWrapperDescriptor(ListWrapper wrapper) : base("Wrapper", null)
{
if (wrapper == null) throw new ArgumentNullException("wrapper");
this.wrapper = wrapper;
}
public override bool ShouldSerializeValue(object component) { return false; }
public override void ResetValue(object component) {
throw new NotSupportedException();
}
public override bool CanResetValue(object component) { return false; }
public override bool IsReadOnly {get {return true;}}
public override void SetValue(object component, object value) {
throw new NotSupportedException();
}
public override object GetValue(object component) {
return ((ListWrapper)component).list;
}
public override Type ComponentType {
get { return typeof(ListWrapper); }
}
public override Type PropertyType {
get { return wrapper.list.GetType(); }
}
public override string DisplayName {
get {
IList list = wrapper.list;
if (list.Count == 0) return "Empty list";
return "List of " + list.Count
+ " " + list[0].GetType().Name;
}
}
}
}
If the serializer you are using does not retain the type - at the least, you must store the type of T along with the data, and use that to create the generic list reflectively:
//during storage:
Type elementType = myList.GetType().GetGenericTypeDefinition().GetGenericArguments[0];
string typeNameToSave = elementType.FullName;
//during retrieval
string typeNameFromDatabase = GetTypeNameFromDB();
Type elementType = Type.GetType(typeNameFromDatabase);
Type listType = typeof(List<>).MakeGenericType(new Type[] { elementType });
Now you have listType, which is the exact List<T> you used (say, List<Foo>). You can pass that type into your deserialization routine.

Categories

Resources