繁体   English   中英

如何序列化接口类型成员

[英]How to serialize interface typed member

我有一个具有定义为接口的属性的类。 我的类的用户可以为此属性分配任何实现该接口的类实现。 我希望能够从磁盘上的文本文件加载此类状态。 用户应该能够手动修改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>

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

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

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


可以序列化以下模型:

public class BaseEngine { }

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

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

更新

我意识到使用基类对你来说并不是一个真正的选择。

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

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

以下是自定义序列化程序的示例,请记住,此示例需要在实际应用程序中完整使用。

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

  1. 异常处理
  2. 将xml元素值转换或转换为行上的正确类型anyThingProperty.SetValue(obj, propertyElement.Value, null);
[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
{
}

基于@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;
    }
}

您可以使用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

这是在线示例

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM