序列化派生类型 - 不在数组中

时间:2016-10-07 11:22:41

标签: c# xmlserializer

通过Visual Studio设置设计器(自动生成的代码)使用.NET XmlSerializer,可以序列化派生类型数组,如下所示:

[XmlArrayItem(Type = typeof(Square)), XmlArrayItem(Type = typeof(Triangle))]
public Shape[] Shapes;

...

Properties.Settings.Default.Save();

这就产生了像

这样的XML
<Shape>
  <Triangle>....</Triangle>
  <Triangle>....</Triangle>
  <Square>....</Square>
</Shape>

但是如果派生类型不在数组(或任何其他集合)中会怎样?

XmlArrayItem替换XmlElement,以下

[XmlElement(Type = typeof(Square)), XmlElement(Type = typeof(Triangle))]
public Shape Window;

有效但产生

<Triangle>....</Triangle>

其中元素名称是类型,而不是属性名称,如果我添加第二个Shape:

[XmlElement(Type = typeof(Square)), XmlElement(Type = typeof(Triangle))]
public Shape Door;
[XmlElement(Type = typeof(Square)), XmlElement(Type = typeof(Triangle))]
public Shape Window;

序列化只是失败 - 毕竟,它如何知道反序列化哪个XML元素进入哪个属性?

我缺少一个属性吗?我可以在不编写代码来自定义序列化的情况下完成这项工作吗?怎么样?

3 个答案:

答案 0 :(得分:2)

答案是在基类上使用XmlInclude而不是在属性上使用XmlElement

[XmlInclude(typeof(Triangle))]
[XmlInclude(typeof(Square))]
public abstract class Shape

这会产生稍微不同的XML。对于数组大小写:

<Shapes>
  <Shape xsi:type="Triangle">....</Shape>
  <Shape xsi:type="Triangle">....</Shape>
  <Shape xsi:type="Square">....</Shape>
</Shapes>

和标量案例:

<Door xsi:type="Square">....</Door>
<Window xsi:type="Triangle">....</Window>

完美。

答案 1 :(得分:1)

[XmlInclude]中所示,在根对象上添加this answer属性是一个很好的解决方案,因为它可以处理数据模型中任何位置类型Square的所有多态属性。

然而,通过[XmlElement]消除多态元素的歧义,也可以使用XmlElementAttribute.ElementName属性处理这种情况:

public class Room
{
    [XmlElement("DoorSquare", Type = typeof(Square)), XmlElement("DoorTriangle", Type = typeof(Triangle))]  
    public Shape Door { get; set; }

    [XmlElement("WindowSquare", Type = typeof(Square)), XmlElement("WindowTriangle", Type = typeof(Triangle))]  
    public Shape Window { get; set; }
}

这会生成以下XML:

<Room>
  <DoorTriangle />
  <WindowSquare />
</Room>

示例fiddle

答案 2 :(得分:-1)

复合形状序列化的初稿:

  [Serializable()]
  [XmlRoot("shape", Namespace = "", IsNullable = false)]
  public abstract class Shape {
    public abstract void Draw();
    [XmlAttribute("name")] public string Name { get; set; }
  }

  [Serializable()]
  [XmlRoot("triangle", Namespace = "", IsNullable = false)]
  public class Triangle : Shape {
    public override void Draw() { }
  }

  [Serializable()]
  [XmlRoot("square", Namespace = "", IsNullable = false)]
  public class Square : Shape {
    public override void Draw() { }
  }

  [Serializable()]
  [XmlRoot("compositeShape", Namespace = "", IsNullable = false)]
  public class CompositeShape : Shape {
    [XmlElement("shape", typeof(Shape))]
    [XmlElement("triangle", typeof(Triangle))]
    [XmlElement("square", typeof(Square))]
    [XmlElement("compositeShape", typeof(CompositeShape))]
    public Shape[] Items { get; set; }

    public override void Draw() { }
  }

使用它像:

var shape = new CompositeShape() {
    Name = "some composite shape",
    Items = new Shape[] {
      new CompositeShape() { 
        Name = "inner composite shape",
        Items = new Shape[] {
          new Triangle() {Name = "level 2 triangle"},
          new Square() {Name="level 2 square"}
        } }, 
        new Triangle() {Name = "level 1 triangle"},
        new Square() {Name="level 1 square"}
    }};
    // serialize ...

示例输出:

<compositeShape name="some composite shape">
  <compositeShape name="inner composite shape">
    <triangle name="level 2 triangle" />
    <square name="level 2 square" />
  </compositeShape>
  <triangle name="level 1 triangle" />
  <square name="level 1 square" />
</compositeShape>

这种方法的缺点是,每次将对象添加到层次结构时,您还必须使用该类型的元素来装饰形状数组,因此不能真正关闭以进行修改...