2

I have the following xml which represents 2 types of plugins, FilePlugin and RegsitryPlugin:

<Client>
  <Plugin Type="FilePlugin">
    <Message>i am a file plugin</Message>
    <Path>c:\</Path>
  </Plugin>
  <Plugin Type="RegsitryPlugin">
    <Message>i am a registry plugin</Message>
    <Key>HKLM\Software\Microsoft</Key>
    <Name>Version</Name>
    <Value>3.5</Value>
  </Plugin>
</Client>

I would like to deserialize the xml into objects. As you can see, the 'Message' element is repeating itself both for the FilePlugin and the RegistryPlugin and i am using inheritance for this:

    abstract class Plugin
    {
        private string _message;
        protected Plugin(MISSING conf)
        {
            // here i need to set my private members like:
            // Message = MISSING.Message;
        }
    }

    class FilePlugin : Plugin
    {
        private string _path;
        public FilePlugin(MISSING config)
            : base(config)
        {
            // Here i need to set my private members like:
            // _path = config.Path;
        }
    }

    class RegistryPlugin : Plugin
    {
        private string _key;
        private string _name;
        private string _value;
        public RegistryPlugin(MISSING config)
            : base(config)
        {
            // Here i need to set my private members like:
            // _key = config.Key;
            // _key = config.Name;
            // _key = config.Value;
        }
    }
}

I need somehow to deserialize the xml, and than to decide according the PluginType Element which Instance to create: i.e: if it is written in the xml the Type=FilePlugin than i need to create

Plugin p1 = new FilePlugin(conf);

if it is written in the xml the Type=RegistryPlugin than i need to create

Plugin p2 = new RegistryPlugin(conf);

Please follow my comments in my code in order to understand the missing parts. Thanks

user829174
  • 6,132
  • 24
  • 75
  • 125

4 Answers4

4

Creating your own deserializer isn't also hard. Here is my solution to this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Xml.Linq;
using System.Reflection;
using System.Text;

namespace WindowsFormsApplication1
{
    public abstract class Plugin
    {
        public string Type { get; set; }
        public string Message { get; set; }
    }

    public class FilePlugin : Plugin
    {
        public string Path { get; set; }
    }

    public class RegsitryPlugin : Plugin
    {
        public string Key { get; set; }
        public string Name { get; set; }
        public string Value { get; set; }
    }

    static class MyProgram
    {
        [STAThread]
        static void Main(string[] args)
        {
            string xmlstr =@"
                <Client>
                  <Plugin Type=""FilePlugin"">
                    <Message>i am a file plugin</Message>
                    <Path>c:\</Path>
                  </Plugin>
                  <Plugin Type=""RegsitryPlugin"">
                    <Message>i am a registry plugin</Message>
                    <Key>HKLM\Software\Microsoft</Key>
                    <Name>Version</Name>
                    <Value>3.5</Value>
                  </Plugin>
                </Client>
              ";

            Assembly asm = Assembly.GetExecutingAssembly();
            XDocument xDoc = XDocument.Load(new StringReader(xmlstr));
            Plugin[]  plugins = xDoc.Descendants("Plugin")
                .Select(plugin =>
                {
                    string typeName = plugin.Attribute("Type").Value;
                    var type = asm.GetTypes().Where(t => t.Name == typeName).First();
                    Plugin p = Activator.CreateInstance(type) as Plugin;
                    p.Type = typeName;
                    foreach (var prop in plugin.Descendants())
                    {
                        type.GetProperty(prop.Name.LocalName).SetValue(p, prop.Value, null);
                    }

                    return p;
                }).ToArray();

            //
            //"plugins" ready to use
            //
        }
    }
}
L.B
  • 114,136
  • 19
  • 178
  • 224
  • I have a question regarding your answer, if i want to add another element to the xml named True. the Allow property can get the values of 'True' or 'False'. I want to latter to convert it to a boolean property in Plugin class, meaning the Plugin will have: 'public bool Allow { get; set; }'. is there a possibility to convert it? – user829174 Feb 18 '12 at 15:55
0

My idea:

  • Add methods ToXml and FromXml to Plugin, and lets this method implementation in Plugin load/save only common properties like Message in your example;
  • Override these methods in derived classes, and call base methods in overridden ones to load common parameters, as well as add custom code to load specific parameters.
  • Create static method in LoadXml in Plugin that reads XML file, gets type name, creates this type instance dynamically with reflection and calls its FromXml method.
Vitalii
  • 4,434
  • 4
  • 35
  • 77
0

You may use what is called a "memento pattern".

The idea is that you have some extra object especially for serialization-deserialization.

So you may have something like:

public class PluginMemento {

     [XmlAttribute] public string Type { get; set; }

     [XmlElement] public string Name { get; set; }
     [XmlElement] public string Message { get; set; }
     [XmlElement] public string Key { get; set; }
     ..... //all the other properties
}

[XmlRootAttribute("Client")]
public class Client {

   [XmlElementAttribute("Plugin")]
   public PluginMemento[] plugins;
}

Now you should be able to deserialize your Xml into the Client type.

Then you can enumerate plugins, and start creating instances based on PluginMemento.Type property (via reflection, or by using a factory or a factory method) by passing the PluginMemento into the constructor of the class specified in the Type property.

The factory method can be very simple:

public static Plugin CreatePlugin(PluginMemento memento) {
    switch(memento.Type) {
        case "FirstPlugin": return FirstPlugin(memento);
        case "SecondPlugin": return SecongPlugin(memento);
    }
}

Reflection may be more smart and interesting, but requires a bit more coding.

Alexey Raga
  • 7,457
  • 1
  • 31
  • 40
  • Why would you need reflection if you only have a limited set of possible types that you know beforehand? – svick Feb 18 '12 at 13:28
  • I don't now, honestly. Some people prefer the way "when you need to solve a problem the technique is to solve even a harder one". Especially in terms of plugins: you may have more in the future, or you may want to make it extensible. We don't know the requirements here. And I don't "prescribe" reflection, I just show different ways of doing it. It is not up to me to decide which one is appropriate. – Alexey Raga Feb 18 '12 at 13:32
  • Thank you for your answer, but if i understand currently, all properties will have to be in the PluginMemento class, FilePlugin, RegistryPlugin and all other plugins that i will make... no? i prefer to separate the properties into their derived classes, any suggestions? – user829174 Feb 18 '12 at 13:51
-1

I would implement the Factory Pattern with a Class which produces you the concrete Plugin.

lhlmgr
  • 2,087
  • 1
  • 22
  • 38