事情从Json的序列化和反序列化说起。
在C#2.0的项目中,以前经常使用Json.Net实现序列化和反序列化。后来从c#3.0中开始使用新增的DataContractJsonSerializer进行json相关的操作。微软提供的原生类库使用上并不复杂,参考一下msdn你很容易就会写出序列化和反序列化的方法,比如经常被人使用的泛型方法如下:
JsonHelper /// <summary> /// 对象转换成json /// </summary> /// <typeparam name="T"></typeparam> /// <param name="jsonObject">需要格式化的对象</param> /// <returns>Json字符串</returns> public static string DataContractJsonSerialize<T>(T jsonObject) { var serializer = new DataContractJsonSerializer(typeof(T)); string json = null; using (var ms = new MemoryStream()) //定义一个stream用来存发序列化之后的内容 { serializer.WriteObject(ms, jsonObject); var dataBytes = new byte[ms.Length]; ms.Position = 0; ms.Read(dataBytes, 0, (int)ms.Length); json = Encoding.UTF8.GetString(dataBytes); ms.Close(); } return json; } /// <summary> /// json字符串转换成对象 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="json">要转换成对象的json字符串</param> /// <returns></returns> public static T DataContractJsonDeserialize<T>(string json) { var serializer = new DataContractJsonSerializer(typeof(T)); var obj = default(T); using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(json))) { obj = (T)serializer.ReadObject(ms); ms.Close(); } return obj; }
不过使用该类库方法的过程中还是不慎意外发现了k_BackingField。
举例之前说一下我们的一个简单的实体类Person,它有如下定义:
Person public class Person { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public DateTime Birthday { get; set; } }
如果Person没有附加任何特性,经测试,可以成功使用上述泛型方法进行处理。
Test var person = new Person { Id = 1, FirstName = "jeff", LastName = "wong", Birthday = new DateTime(1983, 8, 3) }; var json = JsonHelper.DataContractJsonSerialize<Person>(person); Console.WriteLine(json); var result = JsonHelper.DataContractJsonDeserialize<Person>(json); if (result != null) { Console.WriteLine("{0} {1}", result.FirstName, result.LastName); }
而且json生成是正常的字符串:
{"Birthday":"\/Date(428688000000+0800)\/","FirstName":"jeff","Id":1,"LastName":"wong"}
可是当我们把Person实体上面设置Serializable特性后,莫名,我就很不解了:
{"<Birthday>k__BackingField":"\/Date(428688000000+0800)\/","<FirstName>k__BackingField":"jeff","<Id>k__BackingField":1,"<LastName>k__BackingField":"wong"}
搜索了一下,果然早就有人发现了类似的问题。后来上stackoverflow一查,查到好像和自动属性有点关系,大家可以看一下这一篇和这一篇,而且评论比原帖精彩好像国内外大部分都是一致的。
有人说用Reflector反汇编可以看到自动属性生成的字段有前缀,可惜我一直使用ILSpy,兴致勃勃反编译查看了一下,没有没有是真的没有啊。
到这里你可能会想到,自动属性json会有k_BackingField前缀,那传统那种属性的写法呢?
Person [Serializable] public class Person { private int id; public int Id { get { return id; } set { id = value; } } private string firstName; public string FirstName { get { return firstName; } set { firstName = value; } } private string lastName; public string LastName { get { return lastName; } set { lastName = value; } } private DateTime birthday; public DateTime Birthday { get { return birthday; } set { birthday = value; } } }
OK,我们想一块去了,经测试,带Serializable特性的Person类,输出json一点问题没有:
{"birthday":"\/Date(428688000000+0800)\/","firstName":"jeff","id":1,"lastName":"wong"}
但是,请注意大小写(我挖了一个坑,上面的json是序列化属性吗?首字母有没有大写?)。
有没有兼容方式使自动属性输出json也正常呢?很简单,使用DataContract和DataMember属性,哪一种写法输出json都没有问题的:
Person [DataContract] public class Person { [DataMember] public int Id { get; set; } [DataMember] public string FirstName { get; set; } [DataMember] public string LastName { get; set; } [DataMember] public DateTime Birthday { get; set; } }
json输出带有k_BackingField前缀的问题可能非常简单,这里只是善意地提醒,我个人曾经有过“惨痛”的教训,而且感觉还相当隐蔽,大家也要小心啊。
最后,在使用.net framework自带的json有关类库的时候还碰到过特殊符号和时间格式转换的问题,网上有不少文章都讲到,相信不少人也有类似经历,这里就不说了。
参考:
http://geekswithblogs.net/sdorman/archive/2007/08/08/C-3.0-Automatic-Properties.aspx
http://stackoverflow.com/questions/945585/c-sharp-automatic-property-deserialization-of-json
http://www.cnblogs.com/goldarch/archive/2011/04/25/2027071.html
http://computeroverlord.tumblr.com/post/34781472/deserialization-problems-k-backingfield