2017-03-29 10 views
5

以下のプログラムは、C#でXMLをデシリアライズするときに見つけた問題の設計例です。下の例では、同じ名前の型、 'Country'を宣言する2つの別々のアセンブリがあります。型はXML名前空間によって区別されます。 1つの 'Country'要素を含む設定ファイルを逆シリアル化すると、正しい 'Country'タイプが解決されます。しかし、 'Country'要素の 'List'を逆シリアル化すると、間違った 'Country'型が逆シリアル化されます。C#XMLSerializerが間違った型をリストに逆シリアル化しています

class Program 
{ 
    static void Main(string[] args) 
    { 
     XDocument gbConfig = XDocument.Parse(@"<TradingBlocConfiguration> 
               <GreatBritain> 
                <Country/> 
                <Countries> 
                 <Country/> 
                 <Country/>                   
                </Countries> 
                </GreatBritain>                  </TradingBlocConfiguration>"); 


     XDocument euConfig = XDocument.Parse(@"<TradingBlocConfiguration> 
               <EuropeanUnion> 
                <Country/> 
                <Countries> 
                 <Country/> 
                 <Country/>                   
                </Countries> 
                </EuropeanUnion>                  </TradingBlocConfiguration>"); 

     var greatBritainConfiguration = BuildConfig<TradingBlocConfiguration>(gbConfig); 

     // A single 'Country' is always deserialized correctly.. 
     Console.WriteLine("Great Britain Country Type " + greatBritainConfiguration.TradingBlocConfig.MemberCountry.GetType()); 

     // A List of 'Country' is deserialized to the wrong type, depending on what '[XmlElement]' tag is listed first. 
     Console.WriteLine("Great Britain Countries Type " + greatBritainConfiguration.TradingBlocConfig.MemberCountries[0].GetType()); 

     var euConfiguration = BuildConfig<TradingBlocConfiguration>(euConfig); 
     Console.WriteLine("EU Country Type    " + euConfiguration.TradingBlocConfig.MemberCountry.GetType()); 
     Console.WriteLine("EU Countries Type   " + euConfiguration.TradingBlocConfig.MemberCountries[0].GetType()); 

     Console.ReadLine(); 
    } 

    private static T BuildConfig<T>(XDocument doc) where T : class 
    { 
     var stream = new MemoryStream(); 
     doc.Save(stream);  

     T result; 
     using (var reader = new StreamReader(stream)) 
     { 
      stream.Position = 0; 
      var xs = new XmlSerializer(typeof(T)); 
      result = (T)xs.Deserialize(reader); 
     } 

     return result; 
    } 
} 

[XmlRoot("TradingBlocConfiguration")] 
public sealed class TradingBlocConfiguration 
{ 
    [XmlElement("GreatBritain", typeof(GB.GreatBritain))] 
    [XmlElement("EuropeanUnion", typeof(EU.EuropeanUnion))] 
    public TradingBloc TradingBlocConfig { get; set; }   
} 

[XmlRoot] 
[XmlInclude(typeof(GB.GreatBritain))] 
[XmlInclude(typeof(EU.EuropeanUnion))] 
public class BaseCountry { } 

public abstract class TradingBloc 
{ 
    [XmlIgnore] 
    public abstract List<BaseCountry> MemberCountries { get; set; } 

    [XmlIgnore] 
    public abstract BaseCountry MemberCountry { get; set; } 
} 

namespace GB 
{  
    [XmlRoot("GreatBritain")] 
    public class GreatBritain : TradingBloc 
    { 
     [XmlElement("Country", typeof(Country))] 
     public override BaseCountry MemberCountry { get; set; } 

     [XmlArray("Countries")] 
     [XmlArrayItem("Country", typeof(Country))] 
     public override List<BaseCountry> MemberCountries { get; set; } 

     [XmlRoot(Namespace = "GB")] 
     public class Country : BaseCountry { } 
    } 
} 

namespace EU 
{   
    [XmlRoot("EuropeanUnion")] 
    public class EuropeanUnion : TradingBloc 
    { 
     [XmlElement("Country", typeof(Country))] 
     public override BaseCountry MemberCountry { get; set; } 

     [XmlArray("Countries")] 
     [XmlArrayItem("Country", typeof(Country))] 
     public override List<BaseCountry> MemberCountries { get; set; } 

     [XmlRoot(Namespace = "EU")] 
     public class Country : BaseCountry { } 
    } 
} 

あなたが出力上記の例を実行する場合は、次のとおりです。

Great Britain Country Type XmlSerializationTests.GB.GreatBritain+Country 
Great Britain Countries Type XmlSerializationTests.EU.EuropeanUnion+Country 
EU Country Type    XmlSerializationTests.EU.EuropeanUnion+Country 
EU Countries Type   XmlSerializationTests.EU.EuropeanUnion+Country 

「グレートブリテン国の種類は」間違っています。あなたのようなTradingBlocConfigurationクラスの属性[のXmlElement]の順序を変更する場合は、次へ

[XmlRoot("TradingBlocConfiguration")] 
public sealed class TradingBlocConfiguration 
{   
    [XmlElement("EuropeanUnion", typeof(EU.EuropeanUnion))] 
    [XmlElement("GreatBritain", typeof(GB.GreatBritain))] 
    public TradingBloc TradingBlocConfig { get; set; } 
} 

そして、結果の変更:

Great Britain Country Type XmlSerializationTests.GB.GreatBritain+Country 
Great Britain Countries Type XmlSerializationTests.GB.GreatBritain+Country 
EU Country Type    XmlSerializationTests.EU.EuropeanUnion+Country 
EU Countries Type   XmlSerializationTests.GB.GreatBritain+Country 

をここでは英国がよさそうだが、EUは間違っ:)です。リストが間違った型に逆シリアル化された理由を誰でも説明できますか?

+0

XmlArrayをXmlElementに置き換えることができます。 XmlArrayは余分なタグレイヤを作成します。 XmlArrayでは、があります。 XmlElementを使用すると、 jdweng

+0

私はXmlArrayが必要です。これは人為的な例です。実際に私が持っているのはリストです。私はこれを変更することはできません。 – JMc

+0

これはまだリスト/配列であり、生成されるタグの数に依存します。 XmlElementは1つのタグを使用し、XmlArrayは2つのタグを使用します。 – jdweng

答えて

0

解決方法は、xmlnsタグを追加することでした。更新されたコードは正しく動作します。

class Program 
{ 
    static void Main(string[] args) 
    { 
     XDocument gbConfig = XDocument.Parse(@"<TradingBlocConfiguration> 
              <GreatBritain xmlns=""GB""> 
               <Country/> 
               <Countries> 
                <Country/> 
                <Country/>                   
               </Countries> 
               </GreatBritain>                  </TradingBlocConfiguration>"); 


     XDocument euConfig = XDocument.Parse(@"<TradingBlocConfiguration> 
              <EuropeanUnion xmlns=""EU""> 
               <Country/> 
               <Countries> 
                <Country/> 
                <Country/>                   
               </Countries> 
               </EuropeanUnion>                  </TradingBlocConfiguration>"); 

     var greatBritainConfiguration = BuildConfig<TradingBlocConfiguration>(gbConfig); 

     // A single 'Country' is always deserialized correctly.. 
     Console.WriteLine("Great Britain Country Type " + greatBritainConfiguration.TradingBlocConfig.MemberCountry.GetType()); 

     // A List of 'Country' is deserialized to the wrong type, depending on what '[XmlElement]' tag is listed first. 
     Console.WriteLine("Great Britain Countries Type " + greatBritainConfiguration.TradingBlocConfig.MemberCountries[0].GetType()); 

     var euConfiguration = BuildConfig<TradingBlocConfiguration>(euConfig); 
     Console.WriteLine("EU Country Type    " + euConfiguration.TradingBlocConfig.MemberCountry.GetType()); 
     Console.WriteLine("EU Countries Type   " + euConfiguration.TradingBlocConfig.MemberCountries[0].GetType()); 

     Console.ReadLine(); 
    } 

    private static T BuildConfig<T>(XDocument doc) where T : class 
    { 
     var stream = new MemoryStream(); 
     doc.Save(stream); 

     T result; 
     using (var reader = new StreamReader(stream)) 
     { 
      stream.Position = 0; 
      var xs = new XmlSerializer(typeof(T), new Type[] { typeof(GB.GreatBritain.Country) }); 
      result = (T)xs.Deserialize(reader); 
     } 

     return result; 
    } 
} 

[XmlRoot("TradingBlocConfiguration")] 
public sealed class TradingBlocConfiguration 
{ 
    [XmlElement("GreatBritain", typeof(GB.GreatBritain), Namespace = "GB")] 
    [XmlElement("EuropeanUnion", typeof(EU.EuropeanUnion), Namespace = "EU")] 
    public TradingBloc TradingBlocConfig { get; set; } 
} 

[XmlRoot] 
[XmlInclude(typeof(GB.GreatBritain))] 
[XmlInclude(typeof(EU.EuropeanUnion))] 
public class BaseCountry { } 

public abstract class TradingBloc 
{ 
    [XmlIgnore] 
    public abstract List<BaseCountry> MemberCountries { get; set; } 

    [XmlIgnore] 
    public abstract BaseCountry MemberCountry { get; set; } 
} 

namespace GB 
{ 
    [XmlRoot("GreatBritain")] 
    public class GreatBritain : TradingBloc 
    { 
     [XmlElement("Country", typeof(Country))] 
     public override BaseCountry MemberCountry { get; set; } 

     [XmlArray("Countries")] 
     [XmlArrayItem("Country", typeof(Country))] 

     public override List<BaseCountry> MemberCountries { get; set; } 

     [XmlRoot(Namespace = "GB")] 
     public class Country : BaseCountry { } 
    } 
} 

namespace EU 
{ 
    [XmlRoot("EuropeanUnion")] 
    public class EuropeanUnion : TradingBloc 
    { 
     [XmlElement("Country", typeof(Country))] 
     public override BaseCountry MemberCountry { get; set; } 

     [XmlArray("Countries")] 
     [XmlArrayItem("Country", typeof(Country))] 
     public override List<BaseCountry> MemberCountries { get; set; } 

     [XmlRoot(Namespace = "EU")] 
     public class Country : BaseCountry { } 
    } 
} 
関連する問題