下面的程序是我在C#中反序列化XML时发现的一个问题的虚构示例。我有两个不同的程序集,它们声明了一个具有相同名称"Country"的类型,如下例所示。这些类型通过XML命名空间进行区分。当我反序列化包含单个"Country"元素的配置文件时,正确的"Country"类型被解析。但是,如果我反序列化一个"Country"元素的"列表",则会反序列化错误的"Country"类型。
如果您运行上面的示例,输出结果如下:
“大不列颠国家类型”是不正确的。如果你改变TradingBlocConfiguration类中[XmlElement]属性的顺序,如下所示:
然后结果变为:
在这种情况下,英国看起来很好,但欧盟错了 :)。有人能解释一下为什么列表反序列化成了错误的类型吗?
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
在这种情况下,英国看起来很好,但欧盟错了 :)。有人能解释一下为什么列表反序列化成了错误的类型吗?
XmlRoot
中指定的命名空间仅在该元素是根元素时使用。这对我来说看起来像是一个错误 - 要么在创建序列化程序时应该抛出异常,要么就应该正常工作。我只能建议手动实现IXmlSerializable
。 - Charles Mager