我想XML序列化具有(除其他外)具有 IModelObject 类型的 属性 (这是一个接口)的对象。
public class Example { public IModelObject Model { get; set; } }
当我尝试序列化此类的对象时,出现以下错误: “无法序列化Example类型的成员Example.Model,因为它是接口。”
我知道问题是接口无法序列化。但是,直到运行时才知道具体的 Model 对象类型。
可以将 IModelObject 接口替换为抽象或具体类型,并通过XMLInclude使用继承,但这似乎是一个丑陋的解决方法。
有什么建议么?
这只是声明性序列化的固有限制,其中类型信息未嵌入输出中。
在尝试转换<Flibble Foo="10" />回
<Flibble Foo="10" />
public class Flibble { public object Foo { get; set; } }
序列化程序如何知道它应该是一个int,一个字符串,一个double(还是其他)……
为了使这项工作可行,您有几种选择,但是如果您真正不知道直到运行时,最简单的方法可能就是使用XmlAttributeOverrides。
遗憾的是,这仅适用于基类,不适用于接口。您能做的最好的事情就是忽略不足以满足您需求的属性。
如果您真的必须使用接口,则可以使用以下三种选择:
丑陋,令人不快的锅炉板和大量重复,但大多数同类消费者将不必处理该问题:
[XmlIgnore()] public object Foo { get; set; } [XmlElement("Foo")] [EditorVisibile(EditorVisibility.Advanced)] public string FooSerialized { get { /* code here to convert any type in Foo to string */ } set { /* code to parse out serialized value and make Foo an instance of the proper type*/ } }
这可能会成为维护的噩梦…
与第一种选择类似,您可以完全控制事物,但是
重复劳动的问题与第一个相似。
public sealed class XmlAnything<T> : IXmlSerializable { public XmlAnything() {} public XmlAnything(T t) { this.Value = t;} public T Value {get; set;} public void WriteXml (XmlWriter writer) { if (Value == null) { writer.WriteAttributeString("type", "null"); return; } Type type = this.Value.GetType(); XmlSerializer serializer = new XmlSerializer(type); writer.WriteAttributeString("type", type.AssemblyQualifiedName); serializer.Serialize(writer, this.Value); } public void ReadXml(XmlReader reader) { if(!reader.HasAttributes) throw new FormatException("expected a type attribute!"); string type = reader.GetAttribute("type"); reader.Read(); // consume the value if (type == "null") return;// leave T at default value XmlSerializer serializer = new XmlSerializer(Type.GetType(type)); this.Value = (T)serializer.Deserialize(reader); reader.ReadEndElement(); } public XmlSchema GetSchema() { return(null); } }
使用它会涉及到以下内容(在项目P中):
public namespace P { public interface IFoo {} public class RealFoo : IFoo { public int X; } public class OtherFoo : IFoo { public double X; } public class Flibble { public XmlAnything<IFoo> Foo; } public static void Main(string[] args) { var x = new Flibble(); x.Foo = new XmlAnything<IFoo>(new RealFoo()); var s = new XmlSerializer(typeof(Flibble)); var sw = new StringWriter(); s.Serialize(sw, x); Console.WriteLine(sw); } }
这给你:
<?xml version="1.0" encoding="utf-16"?> <MainClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <Foo type="P.RealFoo, P, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"> <RealFoo> <X>0</X> </RealFoo> </Foo> </MainClass>
尽管避免了很多样板,但对于该类用户而言,这显然更加麻烦。
一个快乐的媒体可能正在将XmlAnything的想法合并到第一种技术的“ backing”属性中。这样,大多数繁琐的工作都为您完成,但是该类的消费者不会因为自省而感到困惑。