5

I'm trying to XML serialize/deserialize an object in C#. The catch is that this object is of a type that was not declared in the same assembly as the code that is invoking the serialization. Instead, it comes from an assembly loaded dynamically at runtime, ant thus it is unknown at compilation time to the code that is invoking the serialization.

The type I'm trying to serialize is this:

//Assembly = P.dll
namespace EDFPlugin.Plugin1
{
    [Serializable]
    [XmlRoot(Namespace = "EDFPlugin.Plugin1")] 
    [XmlInclude(typeof(Options))] 
    public class Options
    {
        private string _username;
        private string _password;

        public string Username {
            get { return _username; }
            set { _username = value;}
        }

        public string Password
        {
            get { return _password; }
            set { _password = value; }
        }
    }
}

As I mentioned before, the code I'm using to try to serialize/deserialize this object is located in an assembly that doesn't know about the Options type at compilation time (since it loads P.dll dynamically at runtime). Nevertheless, I managed to serialize the type correctly, by using this code:

//Assembly = A.exe (doesn't know about P.dll at compile time)
object value = GetOptions() //the actual type returned by this method is EDFPlugin.Plugin1.Options !!
XmlSerializer valueSerializer = new XmlSerializer(value.GetType());
valueSerializer.Serialize(writer, value);

basically, as you can see, by calling GetType() I can get around the issue of not having knowledge of the Options type at compilation time, everything works fine.

The problem arises when I try to deserialize:

//Assembly = A.exe (doesn't know about P.dll at compile time)
XmlSerializer valueSerializer = new XmlSerializer(typeof(object)); //have to use object, as I don't know the type in question...
object value = valueSerializer.Deserialize(reader); //throws exception

Since I don't know the type in question beforehand, I basically cannot properly setup the XmlSerializer. Using a generic object, as shown in the code above, generates an exception:

"<Options xmlns='EDFPlugin.Plugin1'> was not expected."

How can I solve this?

Markus Safar
  • 6,324
  • 5
  • 28
  • 44
Master_T
  • 7,232
  • 11
  • 72
  • 144
  • You have to define matching type (not necessarily the same, but name and wanted properties should be presented for successful deserialization), see [related question](http://stackoverflow.com/q/26368990/1997232). Deserialization is all about reflection, if type can't be found by whatever means it can't be deserialized. You will have to *mimic* `EDFPlugins.Plagin1` in `A.exe`. – Sinatr Nov 26 '15 at 13:41
  • @Sinatr: thanks for the info. Unfortunately, I can't do that. The idea is that my program dynamically loads a series of plugins, which can expose serializable options objects like the one I show in the question. My program should be able to save/load those options automatically. However, I have no prior knowledge of what fields a certain plugin might expose in an options object – Master_T Nov 26 '15 at 13:45
  • 1
    I thought you are talking about deserializing when `p.dll` is not loaded yet. If it is loaded, then obtaining type from loaded assembly (disregards how it's loaded) shouldn't be hard (as per @Ksv3n answer). In case of plugins you expose some API so configs are saved by plugins (or otheriwise, plugin supply all information). E.g. make method `GetTypeToSerialize()` which each plugin must implement and you can get type now. – Sinatr Nov 26 '15 at 13:50

1 Answers1

3

A.exe (doesn't know about P.dll at compile time)

So if A.exe knows it at runtime, you shoud be able do load dynamically EDFPlugin.Plugin1

How about :

XmlSerializer valueSerializer = new XmlSerializer(Type.GetType("EDFPlugin.Plugin1.Options")); 
object value = valueSerializer.Deserialize(reader);

But what if I don't know that typename either?

It recommend to put a custom interface to distinguish option class to other class, then you'll be able to filter dynamically and load it with XmlSerializer.

public interface IAmPlugin
{

}

public class Options: IAmPlugin
{
      ......
}

Then :

Assembly assembly = ... // Your Assemblie which contains plugin 

// XmlSerializer needs all possible types to Deserialize an interface 
var possibleTypes = assembly.GetTypes().Where(t => t.IsClass && t.IsAssignableFrom(typeof(IAmPlugin))).ToArray(); 

XmlSerializer serializer = new XmlSerializer(typeof(IAmPlugin), possibleTypes);
object value = valueSerializer.Deserialize(reader);

This assumes you have a empty public constructors on Options class.

Why an interface instead of an Attribute? Because XmlSerializer handles only multiple interface type.

Perfect28
  • 11,089
  • 3
  • 25
  • 45
  • But what if I don't know that typename either? The idea is that I have a series of plugins that can expose a serializable object that my A.exe should be able to save/load. In this case it's the Options type, but it might be anything else, I don't know beforehand. I thought that the [XmlRoot(Namespace = "EDFPlugin.Plugin1")] tag would take care of that (it actually writes the namespace in the XML) but apparently not. – Master_T Nov 26 '15 at 13:48
  • 1
    Thanks, did not know you could specify multiple types to "try" with the XmlSerializer! – Master_T Nov 26 '15 at 14:26