无法从xml反序列化多态列表
本文关键字:多态 列表 反序列化 xml | 更新日期: 2023-09-27 18:08:51
我使用XmlSerializer对Xsd2Code从xsd文件生成的类进行反序列化,这些类具有扩展基本元素的元素。
下面是一个简化的例子:
<?xml version="1.0" encoding="utf-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="Vehicle" abstract="true">
<xs:sequence>
<xs:element name="Manufacturer" type="xs:string" nillable="false" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="Car">
<xs:complexContent>
<xs:extension base="Vehicle">
<xs:sequence>
<xs:element name="Configuration" type="xs:string" nillable="false" />
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="Truck">
<xs:complexContent>
<xs:extension base="Vehicle">
<xs:sequence>
<xs:element name="Load" type="xs:int" nillable="false" />
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:element name="Garage">
<xs:complexType>
<xs:sequence>
<xs:element name="Vehicles" type="Vehicle" minOccurs="0" maxOccurs="unbounded" nillable="false" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
生成的代码:
public partial class Garage
{
public Garage()
{
Vehicles = new List<Vehicle>();
}
public List<Vehicle> Vehicles { get; set; }
}
[System.Xml.Serialization.XmlIncludeAttribute(typeof(Truck))]
[System.Xml.Serialization.XmlIncludeAttribute(typeof(Car))]
public partial class Vehicle
{
public string Manufacturer { get; set; }
}
public partial class Truck : Vehicle
{
public int Load { get; set; }
}
public partial class Car : Vehicle
{
public string Configuration { get; set; }
}
XML: <?xml version="1.0" encoding="utf-8" ?>
<Garage>
<Vehicles>
<Vehicle>
<Manufacturer>Honda</Manufacturer>
<Configuration>Sedan</Configuration>
</Vehicle>
<Vehicle>
<Manufacturer>Volvo</Manufacturer>
<Load>40</Load>
</Vehicle>
</Vehicles>
</Garage>
和反序列化代码:
var serializer = new XmlSerializer(typeof(Garage));
using (var reader = File.OpenText("Settings.xml"))
{
var garage = (Garage)serializer.Deserialize(reader);
var car = garage.Vehicles[0] as Car;
Console.WriteLine(car.Configuration);
}
我在反序列化行上得到一个异常The specified type is abstract: name='Vehicle', namespace='', at <Vehicle xmlns=''>.
。
如果我从XSD中的Vehicle元素中删除抽象属性,我会得到一个空引用异常,因为garage.Vehicles[0]
不能转换为Car
。
我希望能够反序列化,然后转换成Car
和Truck
。我该怎么做呢?
基本问题是XML与XSD不匹配。如果您尝试使用.Net(示例fiddle)验证XML,您将看到以下错误:
The element 'Vehicles' is abstract or its type is abstract.
The element 'Vehicles' has invalid child element 'Vehicle'. List of possible elements expected: 'Manufacturer'.
这些错误有以下含义:
The element 'Vehicles' has invalid child element 'Vehicle'. List of possible elements expected: 'Manufacturer'.
<Vehicles>
列表没有容器元素。相反,它应该是一个重复的元素集合,如下所示:<Garage xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <Vehicles> <Manufacturer>Honda</Manufacturer> <Configuration>Sedan</Configuration> </Vehicles> <Vehicles> <Manufacturer>Volvo</Manufacturer> <Load>40</Load> </Vehicles> </Garage>
对应的c#类是:
public partial class Garage { public Garage() { Vehicles = new List<Vehicle>(); } [XmlElement] public List<Vehicle> Vehicles { get; set; } }
要使用名为
<Vehicle>
的内部元素指定外部包装元素,XSD必须有一个额外的中间元素ArrayOfVehicle
:<?xml version="1.0" encoding="utf-8"?> <xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:complexType name="Vehicle" abstract="true"> <xs:sequence> <xs:element name="Manufacturer" type="xs:string" nillable="false" /> </xs:sequence> </xs:complexType> <xs:complexType name="Car"> <xs:complexContent> <xs:extension base="Vehicle"> <xs:sequence> <xs:element name="Configuration" type="xs:string" nillable="false" /> </xs:sequence> </xs:extension> </xs:complexContent> </xs:complexType> <xs:complexType name="Truck"> <xs:complexContent> <xs:extension base="Vehicle"> <xs:sequence> <xs:element name="Load" type="xs:int" nillable="false" /> </xs:sequence> </xs:extension> </xs:complexContent> </xs:complexType> <!-- BEGIN CHANGES HERE --> <xs:complexType name="ArrayOfVehicle"> <xs:sequence> <xs:element minOccurs="0" maxOccurs="unbounded" name="Vehicle" nillable="true" type="Vehicle" /> </xs:sequence> </xs:complexType> <xs:element name="Garage"> <xs:complexType> <xs:sequence> <xs:element minOccurs="0" maxOccurs="1" name="Vehicles" type="ArrayOfVehicle" /> </xs:sequence> </xs:complexType> </xs:element> <!-- END CHANGES HERE --> </xs:schema>
在你的问题中,你写了这里是一个简化的例子。在编写问题时,是否有可能手动省略了XSD中的额外嵌套级别?
The element 'Vehicles' is abstract or its type is abstract.
您正在尝试通过使用
XmlIncludeAttribute
指定可能的子类型来序列化多态列表。这样做时,XML中的每个多态元素有必要使用w3c标准属性
xsi:type
({http://www.w3.org/2001/XMLSchema-instance}type
的简称)断言其实际类型,如Xsi:类型属性绑定支持中所解释的那样。只有这样,XmlSerializer
才会知道将每个元素反序列化到的正确的具体类型。因此,最终的XML应该是这样的:<Garage xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <Vehicles xsi:type="Car"> <Manufacturer>Honda</Manufacturer> <Configuration>Sedan</Configuration> </Vehicles> <Vehicles xsi:type="Truck"> <Manufacturer>Volvo</Manufacturer> <Load>40</Load> </Vehicles> </Garage>
或者,如果您希望为您的车辆集合使用外部包装元素:
<Garage xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <Vehicles> <Vehicle xsi:type="Car"> <Manufacturer>Honda</Manufacturer> <Configuration>Sedan</Configuration> </Vehicle> <Vehicle xsi:type="Truck"> <Manufacturer>Volvo</Manufacturer> <Load>40</Load> </Vehicle> </Vehicles> </Garage>
后一个XML可以成功地反序列化到当前的
Garage
类中而不会出现错误,如下所示。
Garage
类的实例,填充其车辆列表,并对其进行序列化。这样做之后,您将看到与您试图反序列化的XML不一致。