2009-07-02 72 views
3

我试图通过XmlSerializer反序列化一个自定义类并出现一些问题,事实上我不知道我将要反序列化的类型(它是可插入的),而我很难确定它。C#自定义Xml序列化

我发现this post看起来相似,但不能完全让它与我的方法一起工作,因为我需要反序列化一个XmlSerializable接口。

我目前得到的是形式。请注意,我期望并且需要能够处理通过插件实现的A类和B类。所以如果我可以避免使用IXmlSerializable(我认为我不能),那就太好了。

对于A的ReadXml是我坚持的。但是,如果我可以对改进系统进行其他更改,那么我很乐意这样做。

public class A : IXmlSerializable 
{ 
    public IB MyB { get; set;} 

    public void ReadXml(System.Xml.XmlReader reader) 
    { 
     // deserialize other member attributes 

     SeekElement(reader, "MyB"); 
     string typeName = reader.GetAttribute("Type"); 

     // Somehow need to the type based on the typename. From potentially 
     //an external assembly. Is it possible to use the extra types passed 
     //into an XMlSerializer Constructor??? 
     Type bType = ??? 

     // Somehow then need to deserialize B's Members 
     // Deserialize X 
     // Deserialize Y 
    } 

    public void WriteXml(System.Xml.XmlWriter writer) 
    { 
     // serialize other members as attributes 

     writer.WriteStartElement("MyB"); 
     writer.WriteAttributeString("Type", this.MyB.GetType().ToString()); 
     this.MyB.WriteXml(writer); 
     writer.WriteEndElement(); 
    } 

    private void SeekElement(XmlReader reader, string elementName) 
    { 
     ReaderToNextNode(reader); 
     while (reader.Name != elementName) 
     { 
     ReaderToNextNode(reader); 
     } 
    } 

    private void ReaderToNextNode(XmlReader reader) 
    { 
     reader.Read(); 
     while (reader.NodeType == XmlNodeType.Whitespace) 
     { 
     reader.Read(); 
     } 
    } 
} 

public interface IB : IXmlSerializable 
{ 
} 

public class B : IB 
{ 

    public void ReadXml(XmlReader reader) 
    { 
     this.X = Convert.ToDouble(reader.GetAttribute("x")); 
     this.Y = Convert.ToDouble(reader.GetAttribute("y")); 
    } 

    public void WriteXml(XmlWriter writer) 
    { 
     writer.WriteAttributeString("x", this.X.ToString()); 
     writer.WriteAttributeString("y", this.Y.ToString()); 
    } 
} 

注:更新为我意识到B应该使用接口IB。对不起,有点错误的问题。

回答

4

要从字符串创建实例,请使用Activator.CreateInstance的重载之一。要获得具有该名称的类型,请使用Type.GetType。

+0

这是一个有趣的方法。那么你在那一刻建议调用该创建类型的ReadXml()来初始化成员?这可能工作取决于我是否可以获得类型+创建一个可能的外部类型到我的库。 – Ian 2009-07-03 07:59:48

+0

我回应了你的评论,你说“不知何故需要基于类型名称的类型”。 – 2009-07-03 09:10:15

+0

谢谢约翰。通过从XML中读取类型,创建一个实例,然后我可以使用创建的实例的ReadXml方法对其进行反序列化。作品一种享受,并与我的界面工作正常。 – Ian 2009-07-03 12:06:17

0

我会使用xpath来快速确定输入xml是否包含类A或类B.然后根据它反序列化它。

1

我不认为你需要实现IXmlSerializable ...

既然你不知道运行前的实际类型,可以动态地添加属性覆盖到XmlSerializer的。你只需要知道的是从A继承例如,如果您使用的是作为另一个类的属性类型列表:

public class SomeClass 
{ 
    public A SomeProperty { get; set; } 
} 

您可以动态地应用XmlElementAttribute S代表每个派生类型到该属性:

XmlAttributes attr = new XmlAttributes(); 
var candidateTypes = from t in AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes()) 
        where typeof(A).IsAssignableFrom(t) && !t.IsAbstract 
        select t; 
foreach(Type t in candidateTypes) 
{ 
    attr.XmlElements.Add(new XmlElementAttribute(t.Name, t)); 
} 

XmlAttributeOverrides overrides = new XmlAttributeOverrides(); 
overrides.Add(typeof(SomeClass), "SomeProperty", attr); 

XmlSerializer xs = new XmlSerializer(typeof(SomeClass), overrides); 
... 

这只是一个非常简单的例子,但它说明了如何应用XML序列化在运行时的属性,当你不能做到这一点静态。