當前主流的序列化JSON字符串主要有兩種方式:JavaScriptSerializer及Json.net(Nuget標識:Newtonsoft.Json)。JavaScriptSerializer是微軟官方提供的一種方法,所以如果你用的是asp.net mvc,在Action中如果你返回的語句寫的是”return Json(xxx);“,其實你用的就是JavaScriptSerializer方式。現在更多的人選擇的是Json.net,因為它為用戶提供了更加清晰地使用體驗,清晰在哪?本文主要就是帶你走進它們的世界。
我們先定義一個測試用的簡單類--Person:
public class Person { public string Name; public int Age; public Guid TokenId; public DateTime RegTime; public Person Child; public Person Friend; }
類中的成員僅用來區分不同的變量類型。我們分別以JavaScriptSerializer和Json.net來序列化:
var person = new Person { Age = 28, Name = "李玉寶<yubaolee:>",//故意添加特殊字符 RegTime = DateTime.Now, TokenId = Guid.NewGuid(), Child = new Person { Age = 1, Name = "baby", RegTime = DateTime.Now, TokenId = Guid.NewGuid() } }; //注意這裡面的Friend沒有賦值,默認為空 JavaScriptSerializer serializer = new JavaScriptSerializer(); var jsstr = serializer.Serialize(person); //使用JavaScriptSerializer序列化 string newtonstr = JsonConvert.SerializeObject(person); //使用Json.net序列化
JavaScriptSerializer序列化是先生成一個對象,然後調用它的成員函數Serialize進行序列化;
Json.net直接使用提供的靜態成員JsonConvert.SerializeObject進行序列化;
兩者使用都比較簡單,Json.net調用起來方便那麼一丟丟!我們來看一下控制台輸出結果:
jsstr = Regex.Replace(jsstr, @"\\/Date\((\d+)\)\\/", match => { DateTime dt = new DateTime(1970, 1, 1); dt = dt.AddMilliseconds(long.Parse(match.Groups[1].Value)); dt = dt.ToLocalTime(); return dt.ToString("yyyy-MM-dd HH:mm:ss"); });
處理完成後的效果:
string newtonstr = JsonConvert.SerializeObject(p, Formatting.Indented, new IsoDateTimeConverter() {DateTimeFormat = "yyyy-MM-dd HH:mm:ss"});
2、JavaScriptSerializer序列化會對特殊字符(如<>等)進行編碼,比如上面的\u003c \u003e,很多人看到這個的時候,第一感覺就是太扯蛋了,接下來就是各種百度,怎麼把這個轉成正常的”<>”。實際上你不用做任何操作,這是標准的JS編碼方式,前端會自行處理這個問題。比如:
<script type="text/javascript"> var str = 'yubaolee <yubaolee>' var str2 = 'yubaolee \u003cyubaolee\u003e'; alert(str == str2); //結果為true </script>
附:如果你真的不明白\u003c這到底是個什麼玩意,請移步:字符編碼。
從上面兩點可以看出,JavaScriptSerializer序列化出來的JSON字符串容易給人造成一些困惑,而Json.net完全沒有上面的兩種情況處理。所以現在很多人都在用Json.net,但從Html標准的角度上來看,JavaScriptSerializer序列化出來的結果更符合Html的要求。不過為了操作習慣,還是建議使用Json.net。
我們分別用兩種方式對上面已經成功序列化的兩個字符串進行反序列化:
//對JavaScriptSerializer生成的字符串進行反序列化 //使用JavaScriptSerializer方式 var jsperson = serializer.Deserialize<Person>(jsstr); //使用Json.net方式 var newtonperson = JsonConvert.DeserializeObject<Person>(jsstr); //對Json.net生成的字符串進行反序列化 var jsperson2 = serializer.Deserialize<Person>(newtonstr); var newtonperson2 = JsonConvert.DeserializeObject<Person>(newtonstr);
通過運行會發現4個反序列化代碼都能正常運行,而不是像以前某些前輩說的,JavaScriptSerializer序列化的串只能用它反序列化,Json.net序列化的串只能用Json.net來反序列化。
上面反序列化的字符串是程序生成的,能正常反序列化不足為奇。但通常我們要反序列化的字符串是客戶提交到服務器上面來的串,他們通常是不完整的,或有些還會出現類型不符的情況。比如:
string noChildStr = "{\"Name\":\"李玉寶<yubaolee:>\"," + "\"Age\":28," + "\"RegTime\":\"2015-09-11 00:10:48\"," + "\"Friend\":null}"; var jsperson = new JavaScriptSerializer().Deserialize<Person>(noChildStr); var newtonperson = JsonConvert.DeserializeObject<Person>(noChildStr);
注意這個字符串中,沒有TokenId,沒有Child,而且Friend為null。看一看結果:
string noChildStr = "{\"Name\":\"李玉寶<yubaolee:>\"," + "\"Age\":28," + "\"RegTime\":\"2015-09-11 00:10:48\"," + "\"Friend\":null," + "\"TokenId\":null}"; //注意這個TokenId為空
在運行的時候,程序會直接報錯。
public class PersonJsConverter : JavaScriptConverter { public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer) { Person person = new Person(); object value = null; if (dictionary.TryGetValue("TokenId", out value) && value != null) person.TokenId = (Guid) value; //其他字段略... return person; } public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer) { Dictionary<string, object> dic = new Dictionary<string, object>(); var node = obj as Person; if (node == null) return null; if (!string.IsNullOrEmpty(node.Name)) dic.Add("Name", node.Name); //其他字段略... return dic; } public override IEnumerable<Type> SupportedTypes { get { return new Type[] { typeof(Person) }; } } }
然後在反序列化之前,我們把這個轉換注冊到實體對象中,這時再執行,程序就一切正常了:
JavaScriptSerializer serializer = new JavaScriptSerializer(); serializer.RegisterConverters(new JavaScriptConverter[] { new PersonJsConverter(), }); var deserialize = serializer.Deserialize<Person>(noChildStr);
2、在使用Json.net時,采用了一種更加優雅的方式來處理這個問題--JsonConverter,它可以單獨處理一個指定的類成員變量。這樣就不用像上面的JavaScriptConverter一樣處理整個類的所有成員。代碼如下:
public class GuidConverter : JsonConverter { public override bool CanConvert(Type objectType) { return objectType.IsAssignableFrom(typeof(Guid)); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { try { return serializer.Deserialize<Guid>(reader); } catch { //如果傳進來的值造成異常,則賦值一個初值 return Guid.Empty; } } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { serializer.Serialize(writer, value); } }
值得注意的是JsonConverter是一個Attribute,所以要在類成員上面添加一個特性:
public class Person { public string Name; public int Age; [JsonConverter(typeof(GuidConverter))] public Guid TokenId { get; set; } public DateTime RegTime; public Person Child; public Person Friend; }
這時,再運行程序時,TokenId就會被賦上一個初始的值。看來在反序列化中,Json.net還是更勝一籌。
這是網上給出來兩個的性能比較:
綜上,在不考慮系統對第三方控件引用的要求情況下,盡量使用Json.net來進行對象序列化處理。
說了半天,回顧一下序列化的定義:
序列化:將對象轉換成字節流的過程,這樣就可以輕松將對象保存在磁盤文件或數據庫中。
反序列化:序列化的逆過程,就是將字節流轉換回原來的對象的過程。
其他各種格式序列化的方法請參考:
序列化與反序列化,及Json序列化反序列化
談談:.Net中的序列化和反序列化