2016-09-24 9 views
0

基本要素を拡張する要素を持つxsdファイルからXsd2Codeによって生成されたクラスに対して、XmlSerializerを使用してXMLファイルから逆シリアル化します。ここでxmlからポリモーフィックリストを逆シリアル化できません

は簡単な例です:

<?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シリアライザ= XmlSeriaライザー(typeof(ガレージ));

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にキャストできないため、null参照例外が発生します。

デシリアライズしてからCarTruckにキャストできます。どうすればこの作品を作れますか?

答えて

1

基本的な問題は、XMLがXSDと一致しないことです。あなたはネット(サンプル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'.

    問題ここをあなたのXSDが<Vehicles>のリストにコンテナ要素がないことを指定しているということです。対応するC#クラスがある

    <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> 
    

    いる:

    public partial class Garage 
    { 
        public Garage() 
        { 
         Vehicles = new List<Vehicle>(); 
        } 
    
        [XmlElement] 
        public List<Vehicle> Vehicles { get; set; } 
    } 
    

    あなたのXSD、<Vehicle>という名前の内側の要素を持つ外側のラッパー要素を指定する代わりに、それはちょうどそうのような要素の繰り返し設定する必要があります余分な中間要素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> 
    

    はsimplifiです例。質問を書くときにXSDの入れ子の余分なレベルが手動で省略された可能性はありますか?

  • The element 'Vehicles' is abstract or its type is abstract.

    あなたはXmlIncludeAttributeを使用して可能サブタイプを指定することにより、多型のリストをシリアル化しようとしています。

    これを行う場合、それはXsi:type Attribute Binding Supportに説明されたように、W3C標準属性xsi:type{http://www.w3.org/2001/XMLSchema-instance}typeの略)を使用して実際の型をアサートするXML内の各多型要素のために必要です。その後、XmlSerializerは、各要素を逆シリアル化する正しい具体的な型を認識します。後者のXMLが正常にあなたの現在の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> 
    

    :あなたはあなたの車のコレクションのために、外側ラッパー要素を持っていることを好む場合は、

    <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> 
    

    または:このように、最終的なXMLは次のようになります。エラーなしで、hereと表示されています。

ところで、この種の問題をデバッグするための簡単な方法は、メモリ内にあなたのGarageクラスのインスタンスを作成し、その車両のリストを移入し、それをシリアル化することです。これを実行すると、逆シリアル化しようとしていたXMLとの矛盾が見られるはずです。

+0

返信いただきありがとうございます。確かに、この単純化された例を追加する際に見落とされていた入れ子の問題があります。真のコードはxsdとxmlの両方で正しい入れ子レベルを持っています。私は最終的にあなたが提案した同じxsi:typeソリューションを選択しました。私のXMLは「Vehicles」の短いコレクションを保持しているので、これはうまく動作しますが、巨大なコレクションの場合はどうなりますか?メンテナンスの悪夢かもしれません。 – Dondey

関連する問題