接口属性的 XML 序列化 [英] XML serialization of interface property

查看:19
本文介绍了接口属性的 XML 序列化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I would like to XML serialize an object that has (among other) a property of type IModelObject (which is an interface).

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

当我尝试序列化此类的对象时,收到以下错误:
无法序列化 Example.Model 类型的成员,因为它是一个接口."

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 对象类型直到运行时才知道.

I understand that the problem is that an interface cannot be serialized. However, the concrete Model object type is unknown until runtime.

用抽象或具体类型替换 IModelObject 接口并使用 XMLInclude 继承是可能的,但似乎是一个丑陋的解决方法.

Replacing the IModelObject interface with an abstract or concrete type and use inheritance with XMLInclude is possible, but seems like an ugly workaround.

有什么建议吗?

推荐答案

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

This is simply an inherent limitation of declarative serialization where type information is not embedded within the output.

尝试将 转换回

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

序列化器如何知道它应该是一个int、一个字符串、一个double(或其他)...

How does the serializer know whether it should be an int, a string, a double (or something else)...

要完成这项工作,您有多种选择,但如果您真的不知道直到运行时,最简单的方法很可能是使用 XmlAttributeOverrides.

To make this work you have several options but if you truly don't know till runtime the easiest way to do this is likely to be using the XmlAttributeOverrides.

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

Sadly this will only work with base classes, not interfaces. The best you can do there is to ignore the property which isn't sufficient for your needs.

如果你真的必须坚持使用界面,你有三个真正的选择:

If you really must stay with interfaces you have three real options:

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

Ugly, unpleasant boiler plate and much repetition but most consumers of the class will not have to deal with the problem:

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

这很可能会成为维护的噩梦...

This is likely to become a maintenance nightmare...

类似于第一个选项,您可以完全控制事物,但

Similar to the first option in that you take full control of things but

  • 优点
    • 您不会有令人讨厌的假"属性.
    • 您可以直接与 xml 结构交互以增加灵活性/版本控制
    • 您可能最终不得不为类中的所有其他属性重新实现轮子

    重复劳动的问题与第一个类似.

    Issues of duplication of effort are similar to the first.

    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 中):

    Using this would involve something like (in project 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>
    

    这对于类的用户来说显然更麻烦,但避免了很多样板.

    This is obviously more cumbersome for users of the class though avoids much boiler plate.

    一个愉快的媒介可能是将 XmlAnything 的想法合并到第一种技术的支持"属性中.通过这种方式,大部分繁重的工作都已为您完成,但该类的使用者除了自省的困惑之外不会受到任何影响.

    A happy medium may be merging the XmlAnything idea into the 'backing' property of the first technique. In this way most of the grunt work is done for you but consumers of the class suffer no impact beyond confusion with introspection.

    这篇关于接口属性的 XML 序列化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆