如何序列化接口类型成员

时间:2012-04-20 19:15:32

标签: c# .net xml-serialization

我有一个具有定义为接口的属性的类。 我的类的用户可以为此属性分配任何实现该接口的类实现。 我希望能够从磁盘上的文本文件加载此类状态。用户应该能够手动修改xml文件,以便控制应用程序的操作。

如果我尝试序列化我的类,它会告诉我我无法序列化一个接口。 我知道序列化程序不知道属性类的结构,只知道它实现了一个接口。

我原以为它会在成员上调用GetType,并反映实际类的结构。有没有办法实现这个目标? 还有另一种方法来实现我的要求吗?

修改:澄清我的意图: 让我们说我有这个课程:

class Car
{
IEngine engine
}
class ElectricEngine : IEngine 
{
int batteryPrecentageLeft;
}
class InternalCombustionEngine : IEngine 
{
int gasLitersLeft;
}

并且类用户有一个带

的类
Car myCar = new Car();
myCar.Engine = new ElectricEngine() {batteryPrecentageLeft= 70};

当我序列化myCar类时,我希望xml类似于:

<Car>
<Engine>
<ElectricEngine>
<batteryPrecentageLeft>70</batteryPrecentageLeft>
</ElectricEngine>
<Engine>
</Car>

4 个答案:

答案 0 :(得分:7)

您可以将该属性标记为do-not-include。

但是存在一个更深层次的问题:序列化只能捕获简单的“状态”,而不是行为。你的班级不是可序列化的。反序列化后您对该物业有什么“价值”? null是唯一的选择。

正确的解决方法是考虑实际应该保存什么,并为该部分使用DTO。


可以序列化以下模型:

public class BaseEngine { }

[XmlInclude(typeof(InternalCombustionEngine))]
[XmlInclude(typeof(ElectricEngine))]
public class Car
{      
    public BaseEngine Engine { get; set; }
}

答案 1 :(得分:5)

也许您可以使用基类而不是接口并序列化它。

<强>更新

我意识到使用基类并不适合你。

最好的解决方案可能是与Henk Holterman所说的DTO做一个解决方法。

但如果你真的想要一个问题的解决方案,我认为你必须创建自己的自定义序列化器,但我不建议这样做,因为你最终会遇到很多错误。

以下是自定义序列化程序的示例,请记住此示例将 需要一些工作才能在实际应用中使用。

至少需要添加两件事才能使其不仅仅是一个例子:

  1. 异常处理
  2. 将xml元素值转换或转换为行anyThingProperty.SetValue(obj, propertyElement.Value, null);
  3. 上的正确类型
    [TestClass]
    public class SerializableInterfaceTest
    {
        [TestMethod]
        public void TestMethod1()
        {
            string serialize = AnyThingSerializer.Serialize(
                new SerializableClass {Name = "test", Description = "test1", 
                    AnyThing = new Animal {Name = "test", Color = "test1"}});
            Console.WriteLine(serialize);
            object obj = AnyThingSerializer.Deserialize(serialize);
        }
    }
    
    public sealed class SerializableClass
    {
        public string Name { get; set; }
        public string Description { get; set; }
    
        [AnyThingSerializer]
        public object AnyThing { get; set; }
    }
    
    public static class AnyThingSerializer
    {
        public static string Serialize(object obj)
        {
            Type type = obj.GetType();
            var stringBuilder = new StringBuilder();
            var serializer = new XmlSerializer(type);
            serializer.Serialize(new StringWriter(stringBuilder), obj);
            XDocument doc = XDocument.Load(new StringReader(stringBuilder.ToString()));
            foreach (XElement xElement in SerializeAnyThing(obj))
            {
                doc.Descendants().First().Add(xElement);
            }
            return doc.ToString();
        }
    
        public static object Deserialize(string xml)
        {
            var serializer = new XmlSerializer(typeof (T));
            object obj = serializer.Deserialize(new StringReader(xml));
            XDocument doc = XDocument.Load(new StringReader(xml));
            DeserializeAnyThing(obj, doc.Descendants().OfType().First());
            return obj;
        }
    
        private static void DeserializeAnyThing(object obj, XElement element)
        {
            IEnumerable anyThingProperties = obj.GetType()
                .GetProperties().Where(p => p.GetCustomAttributes(true)
                    .FirstOrDefault(a => a.GetType() == 
                        typeof (AnyThingSerializerAttribute)) != null);
            foreach (PropertyInfo anyThingProperty in anyThingProperties)
            {
                XElement propertyElement = element.Descendants().FirstOrDefault(e => 
                    e.Name == anyThingProperty.Name && e.Attribute("type") != null);
                if (propertyElement == null) continue;
                Type type = Type.GetType(propertyElement.Attribute("type").Value);
                if (IsSimpleType(type))
                {
                    anyThingProperty.SetValue(obj, propertyElement.Value, null);
                }
                else
                {
                    object childObject = Activator.CreateInstance(type);
                    DeserializeAnyThing(childObject, propertyElement);
                    anyThingProperty.SetValue(obj, childObject, null);
                }
            }
        }
    
        private static List SerializeAnyThing(object obj)
        {
            var doc = new List();
            IEnumerable anyThingProperties = 
                obj.GetType().GetProperties().Where(p => 
                    p.GetCustomAttributes(true).FirstOrDefault(a => 
                        a.GetType() == typeof (AnyThingSerializerAttribute)) != null);
            foreach (PropertyInfo anyThingProperty in anyThingProperties)
            {
                doc.Add(CreateXml(anyThingProperty.Name, 
                    anyThingProperty.GetValue(obj, null)));
            }
            return doc;
        }
    
        private static XElement CreateXml(string name, object obj)
        {
            var xElement = new XElement(name);
            Type type = obj.GetType();
            xElement.Add(new XAttribute("type", type.AssemblyQualifiedName));
            foreach (PropertyInfo propertyInfo in type.GetProperties())
            {
                object value = propertyInfo.GetValue(obj, null);
                if (IsSimpleType(propertyInfo.PropertyType))
                {
                    xElement.Add(new XElement(propertyInfo.Name, value.ToString()));
                }
                else
                {
                    xElement.Add(CreateXml(propertyInfo.Name, value));
                }
            }
            return xElement;
        }
    
        private static bool IsSimpleType(Type type)
        {
            return type.IsPrimitive || type == typeof (string);
        }
    }
    
    public class AnyThingSerializerAttribute : XmlIgnoreAttribute
    {
    }
    

答案 2 :(得分:2)

基于@Jens解决方案,我创建了一个能够满足我需要的序列化器。谢谢Jen。 这是代码:

public class RuntimeXmlSerializerAttribute : XmlIgnoreAttribute { }

public class RuntimeXmlSerializer
{
    private Type m_type;
    private XmlSerializer m_regularXmlSerializer;

    private const string k_FullClassNameAttributeName = "FullAssemblyQualifiedTypeName";

    public RuntimeXmlSerializer(Type i_subjectType)
    {
        this.m_type = i_subjectType;
        this.m_regularXmlSerializer = new XmlSerializer(this.m_type);
    }

    public void Serialize(object i_objectToSerialize, Stream i_streamToSerializeTo)
    {
        StringWriter sw = new StringWriter();
        this.m_regularXmlSerializer.Serialize(sw, i_objectToSerialize);
        XDocument objectXml = XDocument.Parse(sw.ToString());
        sw.Dispose();
        SerializeExtra(i_objectToSerialize,objectXml);
        string res = objectXml.ToString();
        byte[] bytesToWrite = Encoding.UTF8.GetBytes(res);
        i_streamToSerializeTo.Write(bytesToWrite, 0, bytesToWrite.Length);
    }

    public object Deserialize(Stream i_streamToSerializeFrom)
    {
        string xmlContents = new StreamReader(i_streamToSerializeFrom).ReadToEnd();
        StringReader sr;
        sr = new StringReader(xmlContents);
        object res = this.m_regularXmlSerializer.Deserialize(sr);
        sr.Dispose();
        sr = new StringReader(xmlContents);
        XDocument doc = XDocument.Load(sr);
        sr.Dispose();
        deserializeExtra(res, doc);
        return res;
    }

    private void deserializeExtra(object i_desirializedObject, XDocument i_xmlToDeserializeFrom)
    {
        IEnumerable propertiesToDeserialize = i_desirializedObject.GetType()
            .GetProperties().Where(p => p.GetCustomAttributes(true)
                .FirstOrDefault(a => a.GetType() ==
                    typeof(RuntimeXmlSerializerAttribute)) != null);
        foreach (PropertyInfo prop in propertiesToDeserialize)
        {
            XElement propertyXml = i_xmlToDeserializeFrom.Descendants().FirstOrDefault(e =>
                e.Name == prop.Name);
            if (propertyXml == null) continue;
            XElement propertyValueXml = propertyXml.Descendants().FirstOrDefault();
            Type type = Type.GetType(propertyValueXml.Attribute(k_FullClassNameAttributeName).Value.ToString());
            XmlSerializer srl = new XmlSerializer(type);
            object deserializedObject = srl.Deserialize(propertyValueXml.CreateReader());
            prop.SetValue(i_desirializedObject, deserializedObject, null);
        }
    }

    private void SerializeExtra(object objectToSerialize, XDocument xmlToSerializeTo)
    {
        IEnumerable propertiesToSerialize =
            objectToSerialize.GetType().GetProperties().Where(p =>
                p.GetCustomAttributes(true).FirstOrDefault(a =>
                    a.GetType() == typeof(RuntimeXmlSerializerAttribute)) != null);
        foreach (PropertyInfo prop in propertiesToSerialize)
        {
            XElement serializedProperty = new XElement(prop.Name);
            serializedProperty.AddFirst(serializeObjectAtRuntime(prop.GetValue(objectToSerialize, null)));
            xmlToSerializeTo.Descendants().First().Add(serializedProperty); //TODO
        }
    }

    private XElement serializeObjectAtRuntime(object i_objectToSerialize)
    {
        Type t = i_objectToSerialize.GetType();
        XmlSerializer srl = new XmlSerializer(t);
        StringWriter sw = new StringWriter();
        srl.Serialize(sw, i_objectToSerialize);
        XElement res = XElement.Parse(sw.ToString());
        sw.Dispose();
        XAttribute fullClassNameAttribute = new XAttribute(k_FullClassNameAttributeName, t.AssemblyQualifiedName);
        res.Add(fullClassNameAttribute);

        return res;
    }
}

答案 3 :(得分:1)

您可以使用ExtendedXmlSerializer。 如果您有课程:

public interface IEngine
{
    string Name {get;set;}
}

public class Car
{
    public IEngine Engine {get;set;}
}


public class ElectricEngine : IEngine 
{
    public string Name {get;set;}
    public int batteryPrecentageLeft {get;set;}
}

public class InternalCombustionEngine : IEngine 
{
    public string Name {get;set;}
    public int gasLitersLeft  {get;set;}
}

并创建此类的实例:

Car myCar = new Car();
myCar.Engine = new ElectricEngine() {batteryPrecentageLeft= 70, Name = "turbo diesel"};

您可以使用ExtendedXmlSerializer序列化此对象:

ExtendedXmlSerializer serializer = new ExtendedXmlSerializer();
var xml = serializer.Serialize(myCar);

输出xml如下所示:

<?xml version="1.0" encoding="utf-8"?>
<Car type="Program+Car">
    <Engine type="Program+ElectricEngine">
        <Name>turbo diesel</Name>
        <batteryPrecentageLeft>70</batteryPrecentageLeft>
    </Engine>
</Car>

您可以从nuget安装ExtendedXmlSerializer或运行以下命令:

Install-Package ExtendedXmlSerializer

这是online example