我使用XmlSerializer从xml文件中反序列化xsd2Code从xsd文件生成的类,其中元素扩展了一个基本元素。
这是一个简化的例子:
context
生成的代码:
<?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>
XML:
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; }
}
反序列化代码:
var serializer = new XmlSerializer(typeof(Garage));
<?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>
我在反序列化行上遇到异常using (var reader = File.OpenText("Settings.xml"))
{
var garage = (Garage)serializer.Deserialize(reader);
var car = garage.Vehicles[0] as Car;
Console.WriteLine(car.Configuration);
}
。
如果我从XSD中的Vehicle元素中删除了抽象属性,我会得到一个空引用异常,因为The specified type is abstract: name='Vehicle', namespace='', at <Vehicle xmlns=''>.
无法强制转换为garage.Vehicles[0]
。
我希望能够反序列化,然后转换为Car
和Car
。我怎样才能做到这一点?
答案 0 :(得分:2)
基本问题是您的XML与您的XSD不匹配。如果您尝试使用.Net验证XML(示例fiddle),您将看到以下错误:
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'.
此处的问题是您的XSD指定<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
的缩写)来声明其实际类型,如{{3}中所述}。只有这样,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
类中,而不会出现错误,如Xsi:type Attribute Binding Support所示。
顺便提一下,调试此类问题的一种简单方法是在内存中创建Garage
类的实例,填充其车辆列表并对其进行序列化。完成后,您会看到与您尝试反序列化的XML不一致。