自定义Xml序列化未知类型

时间:2009-07-02 18:09:01

标签: c# xml-serialization

我试图通过XmlSerializer反序列化一个自定义类并遇到一些问题,因为我不知道我将要反序列化的类型(它是可插入的)并且我遇到了困难确定它。

我发现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。对不起有点错误的问题。

3 个答案:

答案 0 :(得分:3)

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

答案 1 :(得分:1)

我认为您不需要实施IXmlSerializable ...

由于您在运行时之前不知道实际类型,因此可以动态地将属性覆盖添加到XmlSerializer。您只需要知道从A继承的类型列表。例如,如果您使用A作为另一个类的属性:

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

您可以为每个派生类型动态应用XmlElementAttribute到该属性:

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序列化属性。

答案 2 :(得分:0)

我使用xpath快速判断输入xml是包含A类还是B类。然后根据它反序列化。