使用XmlSerializer可以方便的将对象序列化为xml,实现应用之间的数据交互。但是XmlSerializer却不能很好地序列化类型中的可空字段。 
例如,有如下定义的类Person:

  1. [Serializable]
  2. [XmlRoot(ElementName = "Person")]
  3. public class Person
  4. {
  5. public string FirstName { get; set; }
  6. public string LastName { get; set; }
  7. public int? Age { get; set; }
  8. }

其中的Age属性为Nullable int类型。 
我们的实例化代码如下所示:

  1. var person = new Person
  2. {
  3. FirstName = "First",
  4. };
  5. person.OutputXml(Console.Out);

其中方法OutputXml为扩展方法,使用XmlSerializer来序列化对象,具体定义为:

  1. public static void OutputXml<T>(this T dto, TextWriter textWriter)
  2. {
  3. var xmlTypeMapping = typeof(T);
  4. var serializer = new XmlSerializer(xmlTypeMapping);
  5. var xmlns = new XmlSerializerNamespaces();
  6. xmlns.Add(string.Empty, string.Empty);
  7. using (var writer = new XmlTextWriter(textWriter) { Formatting = Formatting.Indented })
  8. {
  9. serializer.Serialize(writer, dto, xmlns);
  10. }
  11. }

使用上述方法序列化对象person,得到的结果为: 
 
注意到虽然Age属性为空,却仍然被序列化成 了古怪的xml,这往往不是所期望的结果。事实上同为空的LastName属性的序列化正是大多数情况下我们所期望的行为——忽略为空的属性。 
另一方面,如果试图序列化类属性为xml 属性(而非xml元素),则甚至不能工作。例如如果我们将Person类的定义修改成如下形式以便序列化为xml属性:

  1. [Serializable]
  2. [XmlRoot(ElementName = "Person")]
  3. public class Person
  4. {
  5. [XmlAttribute]
  6. public string FirstName { get; set; }
  7. [XmlAttribute]
  8. public string LastName { get; set; }
  9. [XmlAttribute]
  10. public int? Age { get; set; }
  11. }

Xmlserializer甚至无法正常序列化上面同样的person对象并抛出如下“XmlAttribute/XmlText cannot be used to encode complex types”的错误: 
 
如何解决上述的2个问题呢? 
1. 序列化可空属性为XmlElement 
为了在序列化可空属性的时候忽略空值的古怪输出,我们可以在Person类中定义一个返回bool的特殊方法ShouldSerializeAge,并在其中实现定义我们的序列化规则:

  1. [Serializable]
  2. [XmlRoot(ElementName = "Person")]
  3. public class Person
  4. {
  5. public string FirstName { get; set; }
  6. public string LastName { get; set; }
  7. public int? Age { get; set; }
  8. public bool ShouldSerializeAge()
  9. {
  10. return Age.HasValue;
  11. }
  12. }

在这里我们定义序列化规则为:序列化非空Age。注意该方法的名字一定是以ShouldSerialize开头并连接上需要自定义规则的属性名称。这样序列化出来的person对象为: 
 
空的Age和空的LastName一样在序列化时被忽略了输出。正是我们期望的结果! 
2. 序列化可空属性为XmlAttribute 
由于可空属性无法被直接序列化为XmlAttribute,我们需要采用间接的办法——定义间接属性。此时我们可以如下定义Person类:

  1. [Serializable]
  2. [XmlRoot(ElementName = "Person")]
  3. public class Person
  4. {
  5. [XmlAttribute]
  6. public string FirstName { get; set; }
  7. [XmlAttribute]
  8. public string LastName { get; set; }
  9. [XmlIgnore]   // (4)
  10. public int? Age { get; set; }
  11. [XmlAttribute(AttributeName = "Age")]  // (1)
  12. public string AgeValue
  13. {
  14. get
  15. {
  16. // (2)
  17. return Age.HasValue ? Age.Value.ToString() : null;
  18. }
  19. set
  20. {
  21. int result;
  22. // (3)
  23. Age = int.TryParse(value, out result) ? result : (int?) null;
  24. }
  25. }
  26. }

注意类中注释的部分:

  1. 为原可空属性定义一个“虚拟”的属性,该属性可为任意名称任意类型(示例里定义的为string类型的AgeValue),但注意要将该属性的序列化名称置为原可空属性Age。
  2. 在get方法里根据Age是否为空将其转化为AgeValue。
  3. 在set方法里将输入的value转化为合适的值赋给可空属性Age。
  4. 将原Age属性标记为XmlIgnore,使XmlSerializer在序列化时忽略序列化该属性。

使用上述方法,我们可以将Age序列化为XmlAttribute了,虽然实际上我们是“骗”了XmlSerializer并使用另一个属性作为Age属性的值。 
因此,当person对象中的Age为空时,我们得到如下的xml结果: 
 
而当person对象中的Age不为空时,我们也可以正常的用XmlAttribute来表示Age的值了。 

使用XmlSerializer序列化可空属性的更多相关文章

  1. XmlSerializer序列化

    XmlSerializer在命名空间using System.Xml.Serialization下. 序列化和反序列化的代码: using System.IO; using System.Xml; u ...

  2. C#.NET如何不序列化字段、属性

    当我们使用公开属性以及公开字段时,都可以顺利的被序列化, 01.[Serializable] 02.public class MyClass 03.{ 04.    public int ID; 05 ...

  3. .NET调用外部接口将得到的List数据,并使用XmlSerializer序列化List对象成XML格式

    BidOpeningData.BidSupervisionSoapClient client = new BidOpeningData.BidSupervisionSoapClient(); Dict ...

  4. 第一章 JacksonUtil 序列化与反序列化属性总结

    1.json-lib与Jackson 关于json-lib与Jackson对比总结如下: 1).性能方面,Jackson的处理能力高出Json-lib10倍左右. 2).json-lib已经停止更新, ...

  5. c# XML序列化与反序列化 属性字段标识

    序列化对象 public class People { [XmlAttribute("NAME")] public string Name { set; get; } [XmlAt ...

  6. 学习C# XmlSerializer 序列化反序列化XML

    类.变量常用头: [XmlRootAttribute]:对根节点的描述,在类声明中使用 如:下例的Html类 [XmlType]:对节点描述,在类声明中使用         如:下例的Head类 [X ...

  7. C# XmlSerializer序列化浅析

    C# 中使用 XmlSerializer 实现类和xml文件的序列化和反序列化,使用起来非常简单. C# XmlSerializer实现序列化: XmlSerializer xml = new Xml ...

  8. C#调用XmlSerializer序列化时生成CDATA节点解决方法

    public class Person{    public string Name { get; set; }    public int Age { get; set; }        } 引用 ...

  9. js如何判断Object是否为空?(属性是否为空)

    js 判断一个 object 对象是否为空 转载原文 判断一个对象是否为空对象,本文给出三种判断方法: 1.最常见的思路,for...in... 遍历属性,为真则为“非空数组”:否则为“空数组” fo ...

随机推荐

  1. 程序猿看小说还要去找TXT?自己动手爬一个TXT才是正确的打开方式

    前言 在贴吧看了个小说追了几天被删帖了,于是自己找书名,打算下载下来看,结果要么是需要充值,要么不提供下载.作为一个猿类,怎么能忍. 好在小说网站多入牛毛,有的采用js加载文字来防采集,有的用css图 ...

  2. jsp实现一条横线中间有字的样式

    实现样式: ---------------------------------------------------- xxxxxx ---------------------------------- ...

  3. hibernate Expression详解

    关键字: hibernate expression hibernate Expression详解Expression.gt:对应SQL条件中的"field > value " ...

  4. 构建Spark的Eclipse开发环境

    前言 无论Windows 或Linux 操作系统,构建Spark 开发环境的思路一致,基于Eclipse 或Idea,通过Java.Scala 或Python 语言进行开发.安装之前需要提前准备好JD ...

  5. 单元测试中Assert类的用法

    Assert类所在的命名空间为Microsoft.VisualStudio.TestTools.UnitTesting 在工程文件中只要引用Microsoft.VisualStudio.Quality ...

  6. 关于Hellas和Greece

    一直以来我就好奇,为什么希腊的中文名字“希腊”和英文名字”Greece”听起来都不像(就像“德国”不像“Germany”一样),而且,为什么在很多体育比赛中看到希腊运动员的衣服上都是“Hellas”, ...

  7. Day Six(Beta)

    站立式会议 站立式会议内容总结 331 今天:完成闹钟功能,远程数据库采用bmob的解决方案,应用初始化bmob 遇到问题:闹钟没有取消提醒 以及多次设置提醒的问题 明天:修改闹钟问题,完成文件下载( ...

  8. git查看提交历史

    git日志的查看 在使用 Git 提交了若干更新之后,又或者克隆了某个项目,想回顾下提交历史,我们可以使用 git log 命令查看. 查看日志信息: $ git log 可以用 --oneline ...

  9. EditText 几种显示方式,固定行数,自适应行数

    1.显示7行,超过7行自动向下补充行数 <EditText android:id="@+id/edt_content" android:layout_width=" ...

  10. Django-Model 使用

    添加 更改 删除 查找 /1 使用get 方式查找,只有一条数据,并如果数据不存在的时候会报错,建议不用 User.objects.get(uname=xxx) User. 模型 get(uname= ...