如何使用继承将xml反序列化为对象?

时间:2012-02-18 13:06:34

标签: c# deserialization

我有以下xml代表两种类型的插件,FilePlugin和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>

我想将xml反序列化为对象。 正如您所看到的,'Message'元素正在为FilePlugin和RegistryPlugin重复,我正在使用继承:

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

我需要以某种方式反序列化xml,而不是根据PluginType元素来决定创建哪个实例: 即: 如果它在xml中写入Type = FilePlugin而不是我需要创建

Plugin p1 = new FilePlugin(conf);

如果它在xml中写入Type = RegistryPlugin而不是我需要创建

Plugin p2 = new RegistryPlugin(conf);

请在我的代码中关注我的评论,以了解缺失的部分。 感谢

4 个答案:

答案 0 :(得分:4)

创建自己的反序列化器并不难。以下是我的解决方案:

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
            //
        }
    }
}

答案 1 :(得分:0)

我的想法:

  • 将方法ToXml和FromXml添加到插件中,并允许插件中的此方法实现仅加载/保存示例中的Message等常用属性;
  • 在派生类中重写这些方法,并在重写的方法中调用基本方法以加载公共参数,以及添加自定义代码以加载特定参数。
  • 在Plugin中的LoadXml中创建静态方法,读取XML文件,获取类型名称,使用反射动态创建此类型实例并调用其FromXml方法。

答案 2 :(得分:0)

您可以使用所谓的“纪念品模式”。

这个想法是你有一些额外的对象,尤其是序列化 - 反序列化。

所以你可能有类似的东西:

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

现在,您应该能够将Xml反序列化为客户端类型。

然后,您可以枚举插件,并通过将PluginMemento传递给Type属性中指定的类的构造函数,开始基于PluginMemento.Type属性(通过反射,或使用工厂或工厂方法)创建实例。 / p>

工厂方法非常简单:

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

反思可能更智能,更有趣,但需要更多编码。

答案 3 :(得分:-1)

我会用一个Class来实现Factory Pattern,它会为你生成具体的插件。