我想用XML序列化一个对象,该对象(除其他外)具有类型为IModelObject的属性(这是一个接口).

public class Example
{
    public IModelObject Model { get; set; }
}

When I try to serialize an object of this class, I receive the following error:
"Cannot serialize member Example.Model of type Example because it is an interface."

我知道问题在于接口无法序列化.然而,具体的Model对象类型在运行时之前是未知的.

IModelObject接口替换为抽象或具体类型并使用XMLInclude继承是可能的,但似乎是一种难看的解决办法.

有什么建议吗?

推荐答案

这只是声明式序列化的一个固有限制,其中类型信息没有嵌入到输出中.

试着把<Flibble Foo="10" />美元兑换成<Flibble Foo="10" />美元

public class Flibble { public object Foo { get; set; } }

序列化程序如何知道它应该是int、string、double(或其他东西)...

要实现这一点,您有几个 Select ,但是如果您真的在运行时才知道,最简单的方法可能是使用XmlAttributeOverrides.

遗憾的是,这只适用于基类,而不适用于接口.在那里,您能做的最好的事情就是忽略不能满足您需要的属性.

如果你真的必须继续使用界面,你有三个真正的 Select :

把它藏起来,在另一个地方处理

难看的、令人不快的样板和大量的重复,但这一类的大多数消费者将不必处理这个问题:

[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*/ } 
}

这可能会成为一场维护噩梦...

实现IXmlSerializable

与第一种 Select 类似,你完全掌控一切,但

  • 赞成的意见
  • 缺点
    • 最终可能需要为类上的所有其他属性重新实现轮子

重复工作的问题与第一个类似.

修改属性以使用包装类型

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 idea 与第一种技术的"支持"特性相结合.通过这种方式,大部分繁重的工作都是为您完成的,但除了与内省的混淆之外,这一类的消费者不会受到任何影响.

.net相关问答推荐

MassTransit RespondAsync 无法返回空值

图像 UriSource 和数据绑定

lock() 是否保证按请求的顺序获得?

Web API 中基于令牌的身份验证,无需任何用户界面

Gacutil.exe 成功添加程序集,但在资源管理器中无法查看程序集.为什么?

如何判断 IOException 是否为 Not-Enough-Disk-Space-Exception 类型?

如何摆脱 VS2008 中的目标程序集不包含服务类型错误消息?

使用返回随机结果的函数进行单元测试

.NET 世界是否有 Maven 替代方案或端口?

C# 中的 myCustomer.GetType() 和 typeof(Customer) 有什么区别?

为什么 LINQ .Where(predicate).First() 比 .First(predicate) 快?

如果需要,将方案添加到 URL

有没有办法只在 C# 中设置一次属性

哪个更快:清除集合或实例化新的

我可以在没有两个查询的情况下通过布尔标准将 IEnumerable 一分为二吗?

如何将 System.Type 转换为其可为空的版本?

仅使用 XAML 绘制纯色三角形

无法加载文件或程序集Antlr3.Runtime (1)或其依赖项之一

判断 .NET 中的目录和文件写入权限

如何在 C# 中处理 XML