小编典典

接口属性的XML序列化

c#

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

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

当我尝试序列化此类的对象时,出现以下错误:
“无法序列化Example类型的成员Example.Model,因为它是接口。”

我知道问题是接口无法序列化。但是,直到运行时才知道具体的 Model 对象类型。

可以将 IModelObject 接口替换为抽象或具体类型,并通过XMLInclude使用继承,但这似乎是一个丑陋的解决方法。

有什么建议么?


阅读 378

收藏
2020-05-19

共1个答案

小编典典

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

在尝试转换<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*/ } 
}

这可能会成为维护的噩梦…

实现IXmlSerializable

与第一种选择类似,您可以完全控制事物,但是

  • 优点
    • 您没有到处都是讨厌的“假”属性。
    • 您可以直接与xml结构进行交互以增加灵活性/版本控制
  • 缺点
    • 您可能最终不得不为类中的所有其他属性重新实现转轮

重复劳动的问题与第一个相似。

修改属性以使用包装类型

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”属性中。这样,大多数繁琐的工作都为您完成,但是该类的消费者不会因为自省而感到困惑。

2020-05-19