使用XmlSerializer序列化可空属性

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

[Serializable]
[XmlRoot(ElementName = "Person")]
public class Person
{
public string FirstName { get; set; }

public string LastName { get; set; }

public int? Age { get; set; }
}

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

var person = new Person
{
FirstName = "First",
};
person.OutputXml(Console.Out);

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

public static void OutputXml<T>(this T dto, TextWriter textWriter)
{
var xmlTypeMapping = typeof(T);
var serializer = new XmlSerializer(xmlTypeMapping);
var xmlns = new XmlSerializerNamespaces();
xmlns.Add(string.Empty, string.Empty);
using (var writer = new XmlTextWriter(textWriter) { Formatting = Formatting.Indented })
{
serializer.Serialize(writer, dto, xmlns);
}
}

使用上述方法序列化对象person,得到的结果为:
[img]http://zz8ss5ww6.iteye.com/upload/picture/pic/94251/9e77a5bf-e3b8-3e3e-8d50-f7a0fbb7649f.png[/img]
注意到虽然Age属性为空,却仍然被序列化成 了古怪的xml,这往往不是所期望的结果。事实上同为空的LastName属性的序列化正是大多数情况下我们所期望的行为——忽略为空的属性。
另一方面,如果试图序列化类属性为xml 属性(而非xml元素),则甚至不能工作。例如如果我们将Person类的定义修改成如下形式以便序列化为xml属性:

[Serializable]
[XmlRoot(ElementName = "Person")]
public class Person
{
[XmlAttribute]
public string FirstName { get; set; }

[XmlAttribute]
public string LastName { get; set; }

[XmlAttribute]
public int? Age { get; set; }
}

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

[Serializable]
[XmlRoot(ElementName = "Person")]
public class Person
{
public string FirstName { get; set; }

public string LastName { get; set; }

public int? Age { get; set; }

public bool ShouldSerializeAge()
{
return Age.HasValue;
}
}

在这里我们定义序列化规则为:序列化非空Age。注意该方法的名字一定是以ShouldSerialize开头并连接上需要自定义规则的属性名称。这样序列化出来的person对象为:
[img]http://zz8ss5ww6.iteye.com/upload/picture/pic/94255/8d100e84-73c9-35c6-92cf-4b3b56b04787.png[/img]
空的Age和空的LastName一样在序列化时被忽略了输出。正是我们期望的结果!
[b]2. 序列化可空属性为XmlAttribute[/b]
由于可空属性无法被直接序列化为XmlAttribute,我们需要采用间接的办法——定义间接属性。此时我们可以如下定义Person类:

[Serializable]
[XmlRoot(ElementName = "Person")]
public class Person
{
[XmlAttribute]
public string FirstName { get; set; }

[XmlAttribute]
public string LastName { get; set; }

[XmlIgnore] // (4)
public int? Age { get; set; }

[XmlAttribute(AttributeName = "Age")] // (1)
public string AgeValue
{
get
{
// (2)
return Age.HasValue ? Age.Value.ToString() : null;
}
set
{
int result;
// (3)
Age = int.TryParse(value, out result) ? result : (int?) null;
}
}
}

注意类中注释的部分:
[list=1]
[*]为原可空属性定义一个“虚拟”的属性,该属性可为任意名称任意类型(示例里定义的为string类型的AgeValue),但注意要将该属性的序列化名称置为原可空属性Age。
[*]在get方法里根据Age是否为空将其转化为AgeValue。
[*]在set方法里将输入的value转化为合适的值赋给可空属性Age。
[*]将原Age属性标记为XmlIgnore,使XmlSerializer在序列化时忽略序列化该属性。
[/list]
使用上述方法,我们可以将Age序列化为XmlAttribute了,虽然实际上我们是“骗”了XmlSerializer并使用另一个属性作为Age属性的值。
因此,当person对象中的Age为空时,我们得到如下的xml结果:
[img]http://zz8ss5ww6.iteye.com/upload/picture/pic/94257/8bd4b85c-354a-3eac-8a48-769b5f46d5bb.png[/img]
而当person对象中的Age不为空时,我们也可以正常的用XmlAttribute来表示Age的值了。
[img]http://zz8ss5ww6.iteye.com/upload/picture/pic/94259/87ccde53-82e5-3e0f-aef9-2fa2f25fa212.png[/img]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值