【问题标题】:How to serialize object with interface typed array property?如何使用接口类型数组属性序列化对象?
【发布时间】:2023-03-03 01:40:01
【问题描述】:

我有一些 XML,我希望使用 XmlSerializer 对其进行序列化和反序列化。我希望将来能够使用其他串行格式,例如 JSON、YAML 等,因此反序列化生成的类应该共享相同的接口。

但是,我的接口包含一个也使用接口的对象数组:

public interface IConfiguration
{
    ICommand[] Commands { get; set; }
}

public Interface ICommand
{
    // Command properties
}

[System.SerializableAttribute()]
public XmlConfiguration : IConfiguration
{
    ICommand[] Commands { get; set; }
}

[System.SerializableAttribute()]
public XmlCommand : ICommand
{
    // Command properties
}

在创建XmlConfiguration 对象时,XML 反序列化操作如何知道使用XmlCommand 具体类型?

一边打字一边思考……

我想我可以向XmlConfiguration 类添加一个构造函数来分配一个具体类型的空数组,但我不确定这是否会按预期工作?

[System.SerializableAttribute()]
class XmlConfiguration : IConfiguration
{
    public XmlConfiguration()
    {
        Commands = new XmlCommand[] { };
    }
}

更新:我意识到XmlArrayItemAttribute 属性可用,但不确定它是否适用于接口:

class XmlConfiguration : IConfiguration
{
    [System.Xml.Serialization.XmlArrayItemAttribute(typeof(XmlCommand))]
    public ICommand[] Commands { get; set; }
}

更新:我也可以这样做:

class XmlConfiguration : IConfiguration
{
    [System.Xml.Serialization.XmlIgnoreAttribute()]
    public ICommand[] Command
    {
        get => CommandsConcrete;
        set => CommandsConcrete = (XmlCommand[])value;
    }

    [System.Xml.Serialization.XmlElementAttribute(ElementName = "Commands")]
    public XmlCommand[] CommandsConcrete { get; set; }
}

【问题讨论】:

  • 您需要以某种方式将类型信息包含到 xml 中,不确定如何使用接口完成,但对于基类,它是 [XmlInclude] 来列出所有继承的类型。您可以切换到基类或使用另一个序列化程序吗?我很确定 json.net 会用TypeNameHandling.Auto 管理它。
  • @Sinatr 我遇到了XmlIncludeAttribute,但 MSDN 页面上的示例代码是空白的......所以我几乎没有什么可以正确应用它:(

标签: c# serialization xml-serialization xmlserializer


【解决方案1】:

要序列化接口属性,一种简单的可能性是使用另一个属性。您仍然需要使用[XmlInclude] 让序列化程序了解所有类型可能发生的情况:

public interface ICommand
{
    string Text { get; set; }
}

public class CommandA : ICommand
{
    public string Text { get; set; }
}

public class CommandB : ICommand
{
    public string Text { get; set; }
}

[XmlInclude(typeof(CommandA))]
[XmlInclude(typeof(CommandB))]
public class Settings
{
    [XmlIgnore]
    public ICommand[] Commands { get; set; }

    [XmlArray(nameof(Commands))]
    public object[] CommandsSerialize
    {
        get { return Commands; }
        set { Commands = value.Cast<ICommand>().ToArray(); }
    }
}

在序列化时会产生

xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Commands>
    <anyType xsi:type="CommandA">
      <Text>a</Text>
    </anyType>
    <anyType xsi:type="CommandB">
      <Text>b</Text>
    </anyType>
  </Commands>
</Settings>

【讨论】:

  • 将属性[XmlArrayItem("Command", typeof(Command))]添加到CommandsSerialize属性,在不需要保留特定类型信息的情况下,可以为&lt;Command&gt;元素提供更好的XML输出
  • 还要注意,作为添加 XmlInclude 属性的替代方法,可以将其他类型传递给 XmlSerializer 构造函数。然而这也有一些问题,最大的问题是构造的序列化器应该被缓存以防止大的内存泄漏
猜你喜欢
  • 2021-08-28
  • 1970-01-01
  • 2012-03-18
  • 1970-01-01
  • 1970-01-01
  • 2013-10-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多