序列化派生类型 - 不在数组中
Serializing derived types - not in an array
通过 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>
其中元素名称是类型,而不是 属性 名称,如果我添加第二个形状:
[XmlElement(Type = typeof(Square)), XmlElement(Type = typeof(Triangle))]
public Shape Door;
[XmlElement(Type = typeof(Square)), XmlElement(Type = typeof(Triangle))]
public Shape Window;
序列化只是失败了——毕竟,它如何在反序列化时知道哪个 XML 元素进入哪个 属性?
我缺少某个属性吗?我可以在不编写代码来自定义序列化的情况下完成这项工作吗?怎么样?
复合形状序列化初稿:
[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>
这种方法的缺点是,每次将对象添加到层次结构时,您还必须使用该类型的元素来装饰形状数组,因此不会真正关闭以进行修改...
答案是在基础 class 上使用 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>
完美。
添加 [XmlInclude]
attributes on the root object as indicated in 是一个很好的解决方案,因为它可以处理数据模型中任何地方出现的所有 Square
类型的多态属性。
然而,这种情况也可以使用 [XmlElement]
attributes by disambiguating the polymorphic elements via 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.
通过 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>
其中元素名称是类型,而不是 属性 名称,如果我添加第二个形状:
[XmlElement(Type = typeof(Square)), XmlElement(Type = typeof(Triangle))]
public Shape Door;
[XmlElement(Type = typeof(Square)), XmlElement(Type = typeof(Triangle))]
public Shape Window;
序列化只是失败了——毕竟,它如何在反序列化时知道哪个 XML 元素进入哪个 属性?
我缺少某个属性吗?我可以在不编写代码来自定义序列化的情况下完成这项工作吗?怎么样?
复合形状序列化初稿:
[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>
这种方法的缺点是,每次将对象添加到层次结构时,您还必须使用该类型的元素来装饰形状数组,因此不会真正关闭以进行修改...
答案是在基础 class 上使用 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>
完美。
添加 [XmlInclude]
attributes on the root object as indicated in Square
类型的多态属性。
然而,这种情况也可以使用 [XmlElement]
attributes by disambiguating the polymorphic elements via 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.