简体   繁体   English

自定义Xml序列化未知类型

[英]Custom Xml Serialization of Unknown Type

I'm attempting to deserialize a custom class via the XmlSerializer and having a few problems, in the fact that I don't know the type that I'm going to be deserializing (it's pluggable) and I'm having difficulty determining it. 我试图通过XmlSerializer反序列化自定义类并遇到一些问题,事实上我不知道我将要反序列化的类型(它是可插入的)并且我很难确定它。

I found this post which looks similar but can't quite get it to work with my approach because I need to deserialize an interface which is XmlSerializable. 我发现这篇文章看起来很相似,但不能完全使用我的方法,因为我需要反序列化一个XmlSerializable的接口。

What I've currently got is of the form. 我目前得到的是形式。 Note that I expect and need to be able to handle both class A and class B to be implemented via a plugin. 请注意,我希望并且需要能够处理通过插件实现的A类和B类。 So if I can avoid using the IXmlSerializable (which I don't think I can) then that would be great. 所以,如果我可以避免使用IXmlSerializable(我认为我不能),那就太棒了。

The ReadXml for A is what I'm stuck on. ReadXml for A就是我所坚持的。 However if there are other changes that I can make to improve the system then I'm happy to do so. 但是,如果我可以进行其他更改以改进系统,那么我很乐意这样做。

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());
   }
}

NOTE : Updated as I realised B was supposed to use interface IB. 注意:更新,因为我意识到B应该使用接口IB。 Sorry for slightly wrong question. 对不起有点错误的问题。

To create an instance from a string, use one of the overloads of Activator.CreateInstance. 要从字符串创建实例,请使用Activator.CreateInstance的重载之一。 To just get a type with that name, use Type.GetType. 要获取具有该名称的类型,请使用Type.GetType。

I don't think you need to implement IXmlSerializable ... 我认为你不需要实现IXmlSerializable ......

Since you don't know the actual types before runtime, you can dynamically add attribute overrides to the XmlSerializer. 由于您在运行时之前不知道实际类型,因此可以动态地将属性覆盖添加到XmlSerializer。 You just need to know the list of types that inherit from A. For instance, if you use A as a property of another class : 您只需要知道从A继承的类型列表。例如,如果您使用A作为另一个类的属性:

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

You can dynamically apply XmlElementAttribute s for each derived type to that property : 您可以为每个派生类型动态地将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);
...

This is just a very basic example, but it shows how to apply XML serialization attributes at runtime when you can't do it statically. 这只是一个非常基本的示例,但它显示了如何在运行时不能静态地应用XML序列化属性。

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

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

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