The program below is a far-fetched example of the problem I found when deserializing XML in C #. I have two separate assemblies that declare a type with the same name "Country" in the example below. Types are distinguished by the XML namespace. When I deserialize a configuration file containing a single "Country" element, then the correct "Country" type is allowed. However, if I deserialize the "List" of the "Country" elements, then the wrong type "Country" will deserialize.
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 { } } }
If you follow the above example, do the following:
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
The "type of UK country" is incorrect. If you change the order of the [XmlElement] attributes in the TradingBlocConfiguration class, for example:
[XmlRoot("TradingBlocConfiguration")] public sealed class TradingBlocConfiguration { [XmlElement("EuropeanUnion", typeof(EU.EuropeanUnion))] [XmlElement("GreatBritain", typeof(GB.GreatBritain))] public TradingBloc TradingBlocConfig { get; set; } }
Then the results change to:
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
In this case, the UK looks good, but the EU is wrong :). Can someone explain why List is being deserialized in the wrong type?