如何使用 .NET XmlSerializer 使值类型可以为空?
Posted
技术标签:
【中文标题】如何使用 .NET XmlSerializer 使值类型可以为空?【英文标题】:How to make a value type nullable with .NET XmlSerializer? 【发布时间】:2010-10-16 17:33:27 【问题描述】:假设我有这个对象:
[Serializable]
public class MyClass
public int Age get; set;
public int MyClassB get; set;
[Serializable]
public class MyClassB
public int RandomNumber get; set;
XmlSerializer 会像这样序列化对象:
<MyClass>
<Age>0</age>
<MyClassB>
<RandomNumber>4234</RandomNumber>
</MyClassB>
</MyClass>
如何使属性 Age 可以为空? IE: 属性 Age 小于 0 时不序列化?
我尝试使用 Nullable,但它像这样序列化我的对象:
<MyClass>
<Age d5p1:nil="true" />
<MyClassB>
<RandomNumber>4234</RandomNumber>
</MyClassB>
</MyClass>
通过阅读 MSDN 文档,我发现:
您不能将 IsNullable 属性应用于类型为值类型的成员,因为值类型不能包含 nullNothingnullptra 空引用(在 Visual Basic 中为 Nothing)。此外,对于可为空的值类型,您不能将此属性设置为 false。当此类类型为 nullNothingnullptra 空引用(Visual Basic 中为 Nothing)时,它们将通过将 xsi:nil 设置为 true 来进行序列化。
来源:http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlelementattribute.isnullable.aspx
我了解值类型不能设置为 null。值类型总是设置为某事。序列化无法根据当前值决定是否序列化。
我尝试了属性,但没有成功。我尝试创建一个 agecontainer 对象并使用属性操作它的序列化,但没有成功。
我真正想要的是:
<MyClass>
<MyClassB>
<RandomNumber>4234</RandomNumber>
</MyClassB>
</MyClass>
当属性 Age 小于 0(零)时。
看来您必须实现自定义序列化。
是的,我也是这么想的,但我想离开它。
在应用程序中,对象要复杂得多,我不想自己处理序列化。
【问题讨论】:
【参考方案1】:我刚刚发现了这一点。 XmlSerialier
查找 XXXSpecified
布尔属性以确定是否应包含它。这应该可以很好地解决问题。
[Serializable]
public class MyClass
public int Age get; set;
[XmlIgnore]
public bool AgeSpecified get return Age >= 0;
public int MyClassB get; set;
[Serializable]
public class MyClassB
public int RandomNumber get; set;
证明:
static string Serialize<T>(T obj)
var serializer = new XmlSerializer(typeof(T));
var builder = new StringBuilder();
using (var writer = new StringWriter(builder))
serializer.Serialize(writer, obj);
return builder.ToString();
static void Main(string[] args)
var withoutAge = new MyClass() Age = -1 ;
var withAge = new MyClass() Age = 20 ;
Serialize(withoutAge); // = <MyClass><MyClassB>0</MyClassB></MyClass>
Serialize(withAge); // = <MyClass><Age>20</Age><MyClassB>0</MyClassB></MyClass>
编辑:是的,这是一个文档化的功能。见MSDN entry for XmlSerializer
另一种选择是使用特殊模式来创建 XmlSerializer 识别的布尔字段,并将 XmlIgnoreAttribute 应用于该字段。该模式以 propertyNameSpecified 的形式创建。例如,如果有一个名为“MyFirstName”的字段,您还将创建一个名为“MyFirstNameSpecified”的字段,指示 XmlSerializer 是否生成名为“MyFirstName”的 XML 元素。
【讨论】:
+1 这是一个巧妙的技巧!您知道它是未记录的功能还是完全受支持? @James 在可空之前就已经存在了,所以它应该可以正常工作:) 我前段时间使用过它,没有遇到任何问题。 y,就在 xml 序列化程序类:msdn.microsoft.com/en-us/library/… --- 在“覆盖默认序列化”的正上方寻找 Specified 哇,这简直是骇人听闻!哈哈。但它是做这项工作的。而且还不错。感谢您的回答。非常感谢。点击为“接受的答案”。 我实际上更喜欢对此进行细微的改动。将 age 设为可为空的 int,然后在 AgeSpecified 属性中条件为 (Age != null)。这使您可以在没有诸如零之类的标记值的情况下获得所需的语义。【参考方案2】:这应该会有所帮助
制作年龄int?
和..
public bool ShouldSerializeAge() return Age.HasValue;
..这确实意味着将 ShouldSerializeXXX 方法添加到您的课程中!
【讨论】:
另外,根据 MSDN 文档,我们应该实现 resetXXX 方法,它会再次将其设置为 null。这两种方法似乎可以协同工作。【参考方案3】:您需要进行自定义 XML 序列化;见IXmlSerializer。
public class MyClass : IXmlSerializable
public int Age get; set;
public MyClassB MyClassB get; set;
public System.Xml.Schema.XmlSchema GetSchema()
// http://msdn.microsoft.com/en-us/library/system.xml.serialization.ixmlserializable.aspx
return null;
public void ReadXml(XmlReader reader)
if (reader.IsStartElement("Age"))
Age = reader.ReadContentAsInt();
var serializer = new XmlSerializer(typeof(MyClassB));
MyClassB = (MyClassB)serializer.Deserialize(reader);
public void WriteXml(XmlWriter writer)
if (Age > 0)
writer.WriteStartElement("Age");
writer.WriteValue(Age);
writer.WriteEndElement();
var serializer = new XmlSerializer(typeof(MyClassB));
serializer.Serialize(writer, MyClassB);
【讨论】:
【参考方案4】:忘记 Nullable ... ShouldSerializeXXX 是一个很好的解决方案。 此处年龄将根据您的情况进行序列化。
[Serializable]
public class MyClass
public int Age get; set;
public int MyClassB get; set;
#region Conditional Serialization
public bool ShouldSerializeAge() return age > 0;
#endregion
[Serializable]
public class MyClassB
public int RandomNumber get; set;
【讨论】:
【参考方案5】:将 Samuel 的回答和 Greg Beech 的评论扩展到布尔属性的情况:如果属性是 bool 类型,那么您不能在 propertySpecified 属性中编写简单的测试。
一个解决方案是使用 Nullable
using System.Xml.Serialization;
public class Person
public bool? Employed get; set;
[XmlIgnore]
public bool EmployedSpecified get return Employed.HasValue;
对数值属性使用可为空类型的替代方法(Greg Beech 建议)是将 value 属性设置为无效的默认值,例如 -1,如下所示:
using System.ComponentModel;
using System.Xml.Serialization;
public class Person
[DefaultValue(-1)]
public int Age get; set;
[XmlIgnore]
public bool AgeSpecified get return Age >= 0;
【讨论】:
【参考方案6】:如果您将元素的 'minoccurs' 属性设置为 'minoccurs="0"',xsd.exe 将自动生成 XXXSpecified 属性和访问器...如果您使用架构来定义您的 xml/类
【讨论】:
【参考方案7】:您可以使用XmlElementAttribute.IsNullable:
[Serializable]
public class MyClass
[XmlElement(IsNullable = true)]
public int? Age get; set;
public int MyClassB get; set;
【讨论】:
以上是关于如何使用 .NET XmlSerializer 使值类型可以为空?的主要内容,如果未能解决你的问题,请参考以下文章
XmlSerializer 空引用异常出现在 .NET 4.0 但不在 .NET 4.5
使用 XmlSerializer 将 XML 反序列化为类型