XmlSerializer 和 IEnumerable:可以进行序列化,无需无参数构造函数:Bug?
在我们的项目中,我们广泛使用 XmlSerializer。偶然我发现了一个没有无参数构造函数的类。我认为这一定会破坏序列化过程,但事实并非如此。
通过调查这个问题,我发现 XmlSerializer 在序列化/反序列化 IEnumerable 时表现得很奇怪:
- 可枚举的所有元素都被序列化
- 该类需要实现 Add(object) em> 方法
- 它忽略此类中可能存在的所有其他属性。
- 它使用此属性调用 getter 并重用返回的实例进行序列化(这允许 XmlSerializer 无需使用无参数构造函数即可工作)。
请看下面的示例。有趣的部分是 ODD1、ODD2。请注意,good5 和 good6 是假的,而我期望它们是真的。
这种行为有原因吗?
在手动实现 IXmlSerialized 时,是否可以让 XmlSerializer 重用属性返回的实例进行反序列化?
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
namespace Test
{
public static class Program
{
public static void Main()
{
HostingClass host = new HostingClass();
host.AutomaticSerialization.StringProperty = "AUTO";
host.SelfImplementedSerialization.StringProperty = "SELF";
bool good1 = host.AutomaticSerialization.FromConstructor == "PARAMETER";
bool good2 = host.SelfImplementedSerialization.FromConstructor == "PARAMETER";
bool good3 = host.AutomaticSerialization.StringProperty == "AUTO";
bool good4 = host.SelfImplementedSerialization.StringProperty == "SELF";
XmlSerializer serializer = new XmlSerializer(typeof(HostingClass));
using (StringWriter sw = new StringWriter())
{
serializer.Serialize(sw, host);
using (StringReader sr = new StringReader(sw.ToString()))
{
host = (HostingClass)serializer.Deserialize(sr);
}
}
bool good5 = host.AutomaticSerialization.FromConstructor == null; //is false
bool good6 = host.AutomaticSerialization.StringProperty == "AUTO"; //is false
bool good7 = host.SelfImplementedSerialization.FromConstructor == null;
bool good8 = host.SelfImplementedSerialization.StringProperty == "SELF";
}
}
public class HostingClass
{
private SelfImplementedSerialization _selfImplementedSerialization;
public SelfImplementedSerialization SelfImplementedSerialization
{
get
{
return _selfImplementedSerialization
?? (_selfImplementedSerialization = new SelfImplementedSerialization("PARAMETER"));
}
set { _selfImplementedSerialization = value; }
}
private AutomaticSerialization _automaticSerialization;
public AutomaticSerialization AutomaticSerialization
{
get
{
return _automaticSerialization
?? (_automaticSerialization = new AutomaticSerialization("PARAMETER")); //the returned object is used while deserializing
}
set { _automaticSerialization = value; }
}
}
public class SelfImplementedSerialization : IXmlSerializable, IEnumerable<int>
{
public SelfImplementedSerialization() { }
public SelfImplementedSerialization(string parameter)
{
FromConstructor = parameter;
}
public string StringProperty { get; set; }
[XmlIgnore]
public string FromConstructor { get; set; }
public void ReadXml(XmlReader reader)
{
reader.ReadStartElement();
StringProperty = reader.ReadElementString("StringProperty");
reader.ReadEndElement();
}
public void WriteXml(XmlWriter writer)
{
writer.WriteElementString("StringProperty", StringProperty);
}
public IEnumerator<int> GetEnumerator()
{
yield return 1;
yield return 2;
}
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
public XmlSchema GetSchema() { return null; }
}
public class AutomaticSerialization : IEnumerable<int>
{
//ODD1: Serialization possible w/o public parameterless constructor
//public AutomaticSerialization() {}
public AutomaticSerialization(string parameter)
{
FromConstructor = parameter;
}
//ODD2: Element not serialized, only the IEnumerable Interface is serialized
[XmlElement("SP")]
public string StringProperty { get; set; }
[XmlIgnore]
public string FromConstructor { get; set; }
public IEnumerator<int> GetEnumerator()
{
yield return 1;
yield return 2;
}
public void Add(object o)
{
//requirement of XmlSerializer when serializing IEnumerables
}
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
}
}
In our project we extensivly use the XmlSerializer. By chance I found a class w/o a parameterless contructor. I thought this must break the serialization process but it did not.
By investigating this issue I found out, that the XmlSerializer behaves strange when serializing/deserializing an IEnumerable:
- All elements of the enumerable are serialized
- The class is required to implement an Add(object) method
- It ignores all other properties that may be in this class.
- It calls a getter with this property and reuses the returned instance for serialization (which allows the XmlSerializer to work w/o a parameterless constructor).
Please have a look at the example that follows. Intersting parts are ODD1, ODD2. Note that good5 and good6 are false, when I expected them to be true.
Is there a reason for this behaviour?
Can I make XmlSerializer reuse an instance returned by a property for deserialization when implementing IXmlSerializable by hand?
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
namespace Test
{
public static class Program
{
public static void Main()
{
HostingClass host = new HostingClass();
host.AutomaticSerialization.StringProperty = "AUTO";
host.SelfImplementedSerialization.StringProperty = "SELF";
bool good1 = host.AutomaticSerialization.FromConstructor == "PARAMETER";
bool good2 = host.SelfImplementedSerialization.FromConstructor == "PARAMETER";
bool good3 = host.AutomaticSerialization.StringProperty == "AUTO";
bool good4 = host.SelfImplementedSerialization.StringProperty == "SELF";
XmlSerializer serializer = new XmlSerializer(typeof(HostingClass));
using (StringWriter sw = new StringWriter())
{
serializer.Serialize(sw, host);
using (StringReader sr = new StringReader(sw.ToString()))
{
host = (HostingClass)serializer.Deserialize(sr);
}
}
bool good5 = host.AutomaticSerialization.FromConstructor == null; //is false
bool good6 = host.AutomaticSerialization.StringProperty == "AUTO"; //is false
bool good7 = host.SelfImplementedSerialization.FromConstructor == null;
bool good8 = host.SelfImplementedSerialization.StringProperty == "SELF";
}
}
public class HostingClass
{
private SelfImplementedSerialization _selfImplementedSerialization;
public SelfImplementedSerialization SelfImplementedSerialization
{
get
{
return _selfImplementedSerialization
?? (_selfImplementedSerialization = new SelfImplementedSerialization("PARAMETER"));
}
set { _selfImplementedSerialization = value; }
}
private AutomaticSerialization _automaticSerialization;
public AutomaticSerialization AutomaticSerialization
{
get
{
return _automaticSerialization
?? (_automaticSerialization = new AutomaticSerialization("PARAMETER")); //the returned object is used while deserializing
}
set { _automaticSerialization = value; }
}
}
public class SelfImplementedSerialization : IXmlSerializable, IEnumerable<int>
{
public SelfImplementedSerialization() { }
public SelfImplementedSerialization(string parameter)
{
FromConstructor = parameter;
}
public string StringProperty { get; set; }
[XmlIgnore]
public string FromConstructor { get; set; }
public void ReadXml(XmlReader reader)
{
reader.ReadStartElement();
StringProperty = reader.ReadElementString("StringProperty");
reader.ReadEndElement();
}
public void WriteXml(XmlWriter writer)
{
writer.WriteElementString("StringProperty", StringProperty);
}
public IEnumerator<int> GetEnumerator()
{
yield return 1;
yield return 2;
}
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
public XmlSchema GetSchema() { return null; }
}
public class AutomaticSerialization : IEnumerable<int>
{
//ODD1: Serialization possible w/o public parameterless constructor
//public AutomaticSerialization() {}
public AutomaticSerialization(string parameter)
{
FromConstructor = parameter;
}
//ODD2: Element not serialized, only the IEnumerable Interface is serialized
[XmlElement("SP")]
public string StringProperty { get; set; }
[XmlIgnore]
public string FromConstructor { get; set; }
public IEnumerator<int> GetEnumerator()
{
yield return 1;
yield return 2;
}
public void Add(object o)
{
//requirement of XmlSerializer when serializing IEnumerables
}
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
出现这种行为的原因是它一直都是这样工作的。
来自 XmlSerializer 类:
The reason for the behavior is that this is the way it has always worked.
From XmlSerializer class: