C # XMLSerializer deserializes wrong type to list

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?

+5
source share
1 answer

The solution was to add xmlns tags. The updated code below works correctly:

 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 { } } } 
0
source

Source: https://habr.com/ru/post/1266065/


All Articles