11

基础项目包含一个抽象基类 Foo。在单独的客户端项目中,有一些类实现了该基类。

我想通过在基类上调用一些方法来序列化和恢复具体类的实例:

// In the base project:
public abstract class Foo
{
    abstract void Save (string path);
    abstract Foo Load (string path);
}

可以假设在反序列化时,所有需要的类都存在。如果可能的话,序列化应该在 XML 中完成。使基类实现 IXmlSerializable 是可能的。

我有点卡在这里。如果我对事物的理解是正确的,那么这只能通过[XmlInclude(typeof(UnknownClass))]为每个实现类添加到基类来实现 - 但实现类是未知的!

有没有办法做到这一点?我没有反思的经验,但我也欢迎使用它的答案。

编辑:问题是序列化。只是序列化会很容易。:-)

4

9 回答 9

9

您也可以在创建 时执行此操作,方法是XmlSerializer在构造函数中提供其他详细信息。请注意,它不会重复使用此类模型,因此您需要配置XmlSerializer一次(在应用程序启动时,从配置中),然后重复使用它......注意XmlAttributeOverrides重载可以进行更多自定义。 .

using System;
using System.Collections.Generic;
using System.IO;
using System.Xml.Serialization;
static class Program
{
    static readonly XmlSerializer ser;
    static Program()
    {
        List<Type> extraTypes = new List<Type>();
        // TODO: read config, or use reflection to
        // look at all assemblies
        extraTypes.Add(typeof(Bar));
        ser = new XmlSerializer(typeof(Foo), extraTypes.ToArray());
    }
    static void Main()
    {
        Foo foo = new Bar();
        MemoryStream ms = new MemoryStream();
        ser.Serialize(ms, foo);
        ms.Position = 0;
        Foo clone = (Foo)ser.Deserialize(ms);
        Console.WriteLine(clone.GetType());
    }
}

public abstract class Foo { }
public class Bar : Foo {}
于 2009-02-26T14:12:52.703 回答
3

您不必将序列化函数放入任何基类中,而是可以将其添加到实用程序类中。

例如(代码仅作为示例,rootName 是可选的)

public static class Utility
{
       public static void ToXml<T>(T src, string rootName, string fileName) where T : class, new()
        {
            XmlSerializer serializer = new XmlSerializer(typeof(T), new XmlRootAttribute(rootName));
            XmlTextWriter writer = new XmlTextWriter(fileName, Encoding.UTF8);
            serializer.Serialize(writer, src);
            writer.Flush();
            writer.Close();
        }
}

只需拨打电话

Utility.ToXml( fooObj, "Foo", @"c:\foo.xml");

不仅 Foo 的家族类型可以使用它,而且所有其他可序列化对象。

编辑

OK 全方位服务...(rootName 是可选的)

public static T FromXml<T>(T src, string rootName, string fileName) where T : class, new()
{
    XmlSerializer serializer = new XmlSerializer(typeof(T), new XmlRootAttribute(rootName));
    TextReader reader = new StreamReader(fileName);
    return serializer.Deserialize(reader) as T;
}
于 2009-02-26T14:12:16.277 回答
2

好吧,序列化应该不是问题,XmlSerializer 构造函数接受一个 Type 参数,即使通过抽象基础上的方法在派生类的实例上调用 GetType 也会返回派生类型的实际 Type。所以本质上只要你在反序列化时知道正确的类型,那么正确类型的序列化是微不足道的。因此,您可以在名为 serialize 的基础上实现一个方法,或者您将什么传递this.GetType()给 XmlSerializer 的构造函数。或者只是将当前引用传递出去,让 serialize 方法处理它,您应该没问题。

编辑:OP编辑更新..

如果您不知道反序列化时的类型,那么您实际上只有一个字符串或字节数组,而在某个地方没有某种标识符,您就有点儿小溪了。您可以做一些事情,例如尝试将 xx 基类的每个已知派生类型反序列化,我不推荐这样做。

您的另一个选择是手动遍历 XML 并通过将类型嵌入为属性或您拥有的东西来重建对象,也许这就是您在文章中最初的意思,但就目前而言,我认为没有办法内置序列化可以在您不指定类型的情况下为您处理此问题。

于 2009-02-26T14:10:38.667 回答
1

在 XML 命名空间深处的某个地方,有一个名为 XmlReflectionImporter 的奇妙类。

如果您需要在运行时创建模式,这可能会对您有所帮助。

于 2009-02-26T14:04:56.450 回答
1

您还可以通过在构造函数的所有可能类型中创建一个 XmlSerializer 密码来执行此操作。请注意,当您使用此构造函数时,每次都会编译 xmlSerializer,如果您不断地重新创建它,则会导致泄漏。您将希望创建一个序列化程序并在您的应用程序中重用它。

然后,您可以引导序列化程序并使用反射查找 foo 的任何后代。

于 2009-02-26T14:10:35.860 回答
1

这些链接可能会对您有所帮助:

我有一个复杂的远程处理项目,并且希望对序列化的 XML 进行非常严格的控制。服务器可以接收它不知道如何反序列化的对象,反之亦然,因此我需要一种快速识别它们的方法。

我尝试过的所有 .NET 解决方案都缺乏项目所需的灵活性。

我在基本 xml 中存储了一个 int 属性来标识对象的类型。

如果我需要从 xml 创建一个新对象,我创建了一个检查类型属性的工厂类,然后创建适当的派生类并将 xml 提供给它。

我做了这样的事情(把它从内存中拉出来,所以语法可能有点不对):

(1) 创建接口

interface ISerialize
{
    string ToXml();
    void FromXml(string xml);       
};

(2) 基类

public class Base : ISerialize
{
    public enum Type
    {
        Base,
        Derived
    };

    public Type m_type;

    public Base()
    {
        m_type = Type.Base;
    }

    public virtual string ToXml()
    {
        string xml;
        // Serialize class Base to XML
        return string;
     }

    public virtual void FromXml(string xml)
    {
        // Update object Base from xml
    }
};

(3) 派生类

public class Derived : Base, ISerialize
{
    public Derived()
    {
         m_type = Type.Derived;
    }

    public override virtual string ToXml()
    {
        string xml;
        // Serialize class Base to XML
        xml = base.ToXml();
        // Now serialize Derived to XML
        return string;
     }
     public override virtual void FromXml(string xml)
     {
         // Update object Base from xml
         base.FromXml(xml);
         // Update Derived from xml
     }
};

(4) 对象工厂

public ObjectFactory
{
    public static Base Create(string xml)
    {
        Base o = null;

        Base.Type t;

        // Extract Base.Type from xml

        switch(t)
        {
            case Base.Type.Derived:
                o = new Derived();
                o.FromXml(xml);
            break;
         }

        return o;
    }
};
于 2009-02-26T14:24:18.790 回答
0

将类标记为 Serializable 并使用Soap BinaryFormatter 而不是 XmlSerializer 将自动为您提供此功能。序列化时将被序列化实例的类型信息写入XML,Soap BinaryFormatter可以在反序列化时实例化子类。

于 2009-02-26T14:17:27.153 回答
0

此方法读取 XML 根元素并检查当前正在执行的程序集是否包含具有此类名称的类型。如果是这样,则对 XML 文档进行反序列化。如果不是,则抛出错误。

public static T FromXml<T>(string xmlString)
{
    Type sourceType;
    using (var stringReader = new StringReader(xmlString))
    {
        var rootNodeName = XElement.Load(stringReader).Name.LocalName;
        sourceType =
                Assembly.GetExecutingAssembly().GetTypes()
                    .FirstOrDefault(t => t.IsSubclassOf(typeof(T)) 
                                    && t.Name == rootNodeName)
                ??
                Assembly.GetAssembly(typeof(T)).GetTypes()
                    .FirstOrDefault(t => t.IsSubclassOf(typeof(T)) 
                                    && t.Name == rootNodeName);

        if (sourceType == null)
        {
            throw new Exception();
        }
    }

    using (var stringReader = new StringReader(xmlString))
    {
        if (sourceType.IsSubclassOf(typeof(T)) || sourceType == typeof(T))
        {
            var ser = new XmlSerializer(sourceType);

            using (var xmlReader = new XmlTextReader(stringReader))
            {
                T obj;
                obj = (T)ser.Deserialize(xmlReader);
                xmlReader.Close();
                return obj;
            }
        }
        else
        {
            throw new InvalidCastException(sourceType.FullName
                                           + " cannot be cast to "
                                           + typeof(T).FullName);
        }
    }
}
于 2011-12-06T17:03:11.750 回答
0

我使用未知(但预期)类的 XmlType 属性来确定反序列化的类型。预期的类型在 AbstractXmlSerializer 类的实例化期间加载并放置在字典中。在反序列化过程中,根元素被读取,并由此从字典中检索类型。之后就可以正常反序列化了。

XmlMessage.class:

public abstract class XmlMessage
{
}

IdleMessage.class:

[XmlType("idle")]
public class IdleMessage : XmlMessage
{
    [XmlElement(ElementName = "id", IsNullable = true)]
    public string MessageId
    {
        get;
        set;
    }
}

AbstractXmlSerializer.class:

public class AbstractXmlSerializer<AbstractType> where AbstractType : class
{
    private Dictionary<String, Type> typeMap;

    public AbstractXmlSerializer(List<Type> types)
    {            
        typeMap = new Dictionary<string, Type>();

        foreach (Type type in types)
        {
            if (type.IsSubclassOf(typeof(AbstractType))) {
                object[] attributes = type.GetCustomAttributes(typeof(XmlTypeAttribute), false);

                if (attributes != null && attributes.Count() > 0)
                {
                    XmlTypeAttribute attribute = attributes[0] as XmlTypeAttribute;
                    typeMap[attribute.TypeName] = type;
                }
            }
        }
    }

    public AbstractType Deserialize(String xmlData)
    {
        if (string.IsNullOrEmpty(xmlData))
        {
            throw new ArgumentException("xmlData parameter must contain xml");
        }            

        // Read the Data, Deserializing based on the (now known) concrete type.
        using (StringReader stringReader = new StringReader(xmlData))
        {
            using (XmlReader xmlReader = XmlReader.Create(stringReader))
            {
                String targetType = GetRootElementName(xmlReader);

                if (targetType == null)
                {
                    throw new InvalidOperationException("XML root element was not found");
                }                        

                AbstractType result = (AbstractType)new
                    XmlSerializer(typeMap[targetType]).Deserialize(xmlReader);
                return result;
            }
        }
    }

    private static string GetRootElementName(XmlReader xmlReader)
    {            
        if (xmlReader.IsStartElement())
        {
            return xmlReader.Name;
        }

        return null;
    }
}

单元测试:

[TestMethod]
public void TestMethod1()
{
    List<Type> extraTypes = new List<Type>();
    extraTypes.Add(typeof(IdleMessage));
    AbstractXmlSerializer<XmlMessage> ser = new AbstractXmlSerializer<XmlMessage>(extraTypes);

    String xmlMsg = "<idle></idle>";

    MutcMessage result = ser.Deserialize(xmlMsg);
    Assert.IsTrue(result is IdleMessage);           
}
于 2016-12-08T08:51:40.843 回答