如何在XML序列化时隐藏可为null的的字段
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何在XML序列化时隐藏可为null的的字段相关的知识,希望对你有一定的参考价值。
使用XmlSerializer可以方便的将对象序列化为xml,实现应用之间的数据交互。但是XmlSerializer却不能很好地序列化类型中的可为null的字段。例如,有如下定义的类Person:
[Serializable]
[XmlRoot(ElementName = "Person")]
public class Person
public string FirstName get; set;
public string LastName get; set;
public int? Age get; set;
[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);
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);
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,得到的结果为:
注意到虽然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;
[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”的错误:
如何解决上述的2个问题呢?
1. 序列化可为null属性输出为XmlElement
为了在序列化可空属性的时候忽略空值的古怪输出,我们可以在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;
[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对象为:
空的Age和空的LastName一样在序列化时被忽略了输出。正是我们期望的结果!
2. 序列化可为null的属性输出为XmlAttribute
由于可空属性无法被直接序列化为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;
[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;
参考技术A webservice这个好像不直接支持json的,一个比较好的替代方法是在C#端先把返回值序列化成json字符串在返回了, 但是这可能会限制程序的易用性和伸缩性,不过要是仅仅简单的返回数据的话,应该没有任何问题,这个可能需要你定义一些简单的协议
如何在 Protobuf-Net 中保留一个可为空值的数组?
【中文标题】如何在 Protobuf-Net 中保留一个可为空值的数组?【英文标题】:How can I persist an array of a nullable value in Protobuf-Net? 【发布时间】:2013-10-01 09:06:27 【问题描述】:我正在从 BinaryFormatter 迁移到 Protobuf-net(到目前为止,它似乎在存储大小和反序列化时间方面都提供了巨大的改进)。
然而,我遇到的一个问题是 double?[] 数组不会以它们被序列化的相同形式反序列化。数组中任何为 null 的值都会被完全删除 - 即,如果我从一个包含 [null, null, 1, 2, 3, null] 的 6 个元素的数组开始,在反序列化之后我会得到一个 [1 , 2, 3]。对于我的程序,我必须以与序列化之前完全相同的形式检索这些数组 - 如果使用 BinaryFormatter 就会发生这种情况。
到目前为止,我提出的一个解决方案是为每个数组创建两个数组,一个是 double[],每个元素都有一个值,另一个是 bool[],它可以用来描述原始值是否为null - 但是由于各种原因,这非常低效。
我可以看到在之前的相关问题中提到,ProtoMember 可能有一个“SupportNull”选项,但是我找不到任何文档清楚地显示如何实现这一点,并且无法自己解决这个问题.
任何人都可以提供任何帮助将不胜感激。
【问题讨论】:
【参考方案1】:using ProtoBuf;
using ProtoBuf.Meta;
using System;
[ProtoContract]
class Foo
[ProtoMember(1)]
public double?[] Values get; set;
static class Program
static void Main()
// configure the model; SupportNull is not currently available
// on the attributes, so need to tweak the model a little
RuntimeTypeModel.Default.Add(typeof(Foo), true)[1].SupportNull = true;
// invent some data, then clone it (serialize+deserialize)
var obj = new Foo Values = new double?[] 1,null, 2.5, null, 3;
var clone = Serializer.DeepClone(obj);
// check we got all the values back
foreach (var value in clone.Values)
Console.WriteLine(value);
【讨论】:
感谢 Marc 提供的优秀样品。但是最好有一个属性。实际上,在静态构造函数中设置它并不总是在反序列化时调用。然后我们必须将“SupportNull”设置在它应该在的类之外的其他地方。但这样做违背了良好编程的主要原则:高内聚和低耦合。【参考方案2】:它实际上不支持开箱即用,您必须操纵RuntimeTypeModel
来明确设置它应该允许空值。
RuntimeTypeModel.Default[typeof(YourObjectType)][(tag)].SupportNull = true;
例子:
var nullable = new ObjectWithNullables() IntArray = new int?[] null, 1, 2, null ;
// returns 2 elements out of 4
//var resultA = Deserialize<ObjectWithNullables>(Serialize<ObjectWithNullables>(nullable));
RuntimeTypeModel.Default[typeof(ObjectWithNullables)][1].SupportNull = true;
// returns 4 elements out of 4
var resultA = Deserialize<ObjectWithNullables>(Serialize<ObjectWithNullables>(nullable));
[ProtoContract]
public class ObjectWithNullables
[ProtoMember(1)]
public int?[] IntArray get; set;
【讨论】:
@user2262704 添加了我的意思的示例 其实是支持的。 @MarcGravell 刚刚找到了相同的解决方案 ^^ 更新了我的答案以上是关于如何在XML序列化时隐藏可为null的的字段的主要内容,如果未能解决你的问题,请参考以下文章
使用 XmlSerializer 将空 xml 属性值反序列化为可为空的 int 属性