[C#]Json.NET - A high performance Json library

[C#]Json.NET - A high performance Json library

 image

 

Json.NET是一個高效能的Json函式庫,提供開發人員針對Json格式開發所需的功能。該函式庫具備有以下的特點:

  • Flexible JSON serializer for converting between .NET objects and JSON
  • LINQ to JSON for manually reading and writing JSON
  • High performance, faster than .NET's built-in JSON serializers
  • Write indented, easy to read JSON
  • Convert JSON to and from XML
  • Supports .NET 2, .NET 3.5, .NET 4, Silverlight, Windows Phone and Windows 8 Metro.

 

簡單的說該函式庫提供了像是將物件序列化成Json字串、將Json字串解序列化為物件、Linq to Json、Json與Xml格式互相轉換...等功能。

 

這些功能可能部分可以用現成BCL的功能下去處理,但是據Json.NET官方的資料來看,它能提供較好的效能,像是下圖的官方效能比較圖,它的的確確是比內建的DataContractJsonSerializer與JavaScriptSerializer快了好幾倍。

image

 

Json.NET在使用前,如同一般的第三方元件,我們必須要將對應的組件加入參考,這動作可以直接透過NuGet下去完成,在Manage NuGet Packages對話框中的搜尋框輸入json.net的關鍵字,點擊Json.NET後方的install按鈕後按下ok離開即可。這邊沒安裝NuGet的也可以直接到Json.NET官方網站下載,並將對應的組件加入參考。

image

 

Json.NET在使用上也十分簡單,若要做物件的序列化,或是要將Json字串解序列化回物件,我們首先必須要有用來作序列化處理的物件,然後透過JsonConvert.SerializeObject與JsonConvert.DeserializeObject這兩道方法下去處理。


var json = JsonConvert.SerializeObject(obj, Formatting.Indented);
...
var person = JsonConvert.DeserializeObject<Person>(json);
...

 

這邊簡單的以幾個小小的程式碼片段來做些說明,假設今天我們有個Person類別,裡面有Name、NickName、Birthday、與Age這幾個成員屬性,對應到現實中,代表的就是一個人的名字、暱稱、生日、與年齡。


		{
			public String Name { get; set; }

			public String NickName { get; set; }

			public DateTime Birthday { get; set; }

			public int Age 
			{
				get
				{
					return (int)((DateTime.Now - Birthday).TotalDays / 365);
				}
			}
		}

 

以Person類別建立物件實體後,填入成員屬性值,再呼叫JsonConvert.SerializeObject將物件序列化為json字串。


			DateTime date = DateTime.Now;

			Person Larry = new Person
			{
				Name = "Larry Nung",
				NickName = "蹂躪",
				Birthday = new DateTime(1980,4,19)
			};

			var json = JsonConvert.SerializeObject(Larry, Formatting.Indented);
			...
			var person = JsonConvert.DeserializeObject<Person>(json);
			...

 

運行後我們可以看到序列化出來的Json字串會像下面這樣:


  "Name": "Larry Nung",
  "NickName": "蹂躪",
  "Birthday": "1980-04-19T00:00:00",
  "Age": 31
}

 

在拿來序列化的類別未附加有什麼特別的Attribute時,預設是所有的成員屬性都會被序列化。它的效果就跟在類別前面附加[JsonObject(MemberSerialization.OptOut)]是一樣的,適合用於類別中大部分的成員屬性都想要被序列化的情境。


		public class Person
		{
			public String Name { get; set; }

			public String NickName { get; set; }

			public DateTime Birthday { get; set; }

			public int Age 
			{
				get
				{
					return (int)((DateTime.Now - Birthday).TotalDays / 365);
				}
			}
		}

 

若是部分屬性不想被序列化,像是這邊的Age成員屬性就可以透過Birthday下去推算,可以忽略不序列化,這時我們可為其加上JsonIgnoreAttribure,指定該成員屬性在序列化時忽略不處理。


		public class Person
		{
			public String Name { get; set; }

			public String NickName { get; set; }

			public DateTime Birthday { get; set; }

			[JsonIgnore]
			public int Age 
			{
				get
				{
					return (int)((DateTime.Now - Birthday).TotalDays / 365);
				}
			}
		}

 

序列化出來的Json字串就不會含有該成員屬性的部分。


  "Name": "Larry Nung",
  "NickName": "蹂躪",
  "Birthday": "1980-04-19T00:00:00"
}

 

那若是今天有個類別,它大部分的成員屬性我們都不想序列化時要怎麼辦?Json.NET也提供對應的設定,在類別前面附加[JsonObject(MemberSerialization.OptIn)]就可以了,這樣在序列化時就只有附加有JsonPropertyAttribute的成員屬性才會被序列化。


		public class Person
		{
			[JsonProperty]
			public String Name { get; set; }

			[JsonProperty]
			public String NickName { get; set; }

			[JsonProperty]
			public DateTime Birthday { get; set; }

			public int Age 
			{
				get
				{
					return (int)((DateTime.Now - Birthday).TotalDays / 365);
				}
			}
		}

 

若有需要也可以直接用BCL現有的DataContractAttribute與DataMemberAttribute,Json.NET也認得這兩個Attribute,運作起來的效果是一樣的。


		public class Person
		{
			[DataMember]
			public String Name { get; set; }

			[DataMember]
			public String NickName { get; set; }

			[DataMember]
			public DateTime Birthday { get; set; }

			public int Age 
			{
				get
				{
					return (int)((DateTime.Now - Birthday).TotalDays / 365);
				}
			}
		}

 

除了使用對應的類別下去做序列化與解序列化的處理,Json.NET也能用內建的JObject、JProperty、與JArray動態的去處理,使用上不需要為Json產生對應的類別,用起來跟Linq to Xml很類似。


			var Larry = new JObject(
				new JProperty("Name", "Larry Nung"),
				new JProperty("NickName", "蹂躪")
				);

			json = JsonConvert.SerializeObject(Larry, Formatting.Indented);
			Console.WriteLine(json);
			...

 

這樣的動態處理方式支援Linq的查詢操作,可以撰寫類似下面的程式語法透過Linq去做些過濾。


			{
				Blogers = new List<Person>{
					new Person()
					{
						Name = "Larry Nung",
						NickName = "蹂躪"
					},
					new Person()
					{
						Name = "Bill Chung",
						NickName = "Bill uncle"
					}
				}
			};

			var linq = from item in JObject.FromObject(dotBlog)["Blogers"]
					   select item;

			foreach (var item in linq)
			{
				Console.WriteLine(item.ToString());
			}

 

若覺得以上的Json字串處理方式太過高階,覺得無法有效的控制建置的細節的話,Json.NET也可以使用類似XmlTextWriter的方式下去組成Json字串,使用起來是會較上面的方法還要麻煩些,但能做較為細微的操作。


			StringBuilder sb = new StringBuilder();
			StringWriter sw = new StringWriter(sb);

			using (JsonWriter jsonWriter = new JsonTextWriter(sw))
			{
				jsonWriter.Formatting = Formatting.Indented;

				jsonWriter.WriteStartObject();
				jsonWriter.WritePropertyName("Name");
				jsonWriter.WriteValue("Larry Nung");
				jsonWriter.WritePropertyName("NickName");
				jsonWriter.WriteValue("蹂躪");
				jsonWriter.WriteEndObject();
			}
			var json = sb.ToString();
			Console.WriteLine(json);
			...

 

除了以上介紹的基本操作外,Json.NET也提供了許多其他的Attribute,像是OnSerializingAttribute、OnSerializedAttribute...等,能指定在序列化、解序列化、序列化錯誤之類的動作被觸發時所要執行的Callback方法,這邊筆者就不多做介紹了,可直接參閱Json.NET - Quick Starts & API Documentation官方文件。最後附上筆者在測試用的範例程式,有興趣的可以拿來做些修改測試看看:


using System.Collections.Generic;
using System.Linq;
using System.Text;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using System.Runtime.Serialization;
using Newtonsoft.Json.Serialization;
using Newtonsoft.Json.Linq;
using System.IO;
using System.Collections;

namespace ConsoleApplication7
{
	class Program
	{
		[DataContract]
		public class Person
		{
			[DataMember]
			public String Name { get; set; }

			[DataMember]
			public String NickName { get; set; }

			[DataMember]
			public DateTime Birthday { get; set; }

			public int Age 
			{
				get
				{
					return (int)((DateTime.Now - Birthday).TotalDays / 365);
				}
			}

			private void ShowMessage(string eventName, StreamingContext context)
			{
				Console.WriteLine("{0}...", eventName);
				Console.WriteLine("Context: {0}", context.Context);
				Console.WriteLine("State: {0}", context.State);
				Console.WriteLine();
			}

			[OnSerializing]
			internal void OnSerializingMethod(StreamingContext context)
			{
				ShowMessage("OnSerializingMethod", context);
			}

			[OnSerialized]
			internal void OnSerializedMethod(StreamingContext context)
			{
				ShowMessage("OnSerializedMethod", context);
			}

			[OnDeserializing]
			internal void OnDeserializingMethod(StreamingContext context)
			{
				ShowMessage("OnDeserializingMethod", context);
			}

			[OnDeserialized]
			internal void OnDeserializedMethod(StreamingContext context)
			{
				ShowMessage("OnDeserializedMethod", context);
			}

			[OnError]
			internal void OnError(StreamingContext context, ErrorContext errorContext)
			{
				ShowMessage("OnError", context);
			}
		}

		static void Main(string[] args)
		{
			DateTime date = DateTime.Now;

			Person Larry1 = new Person
			{
				Name = "Larry Nung",
				NickName = "蹂躪",
				Birthday = new DateTime(1980,4,19)
			};

			var json = JsonConvert.SerializeObject(Larry1, Formatting.Indented);
			Console.WriteLine("Serialized json string...");
			Console.WriteLine(json);
			Console.WriteLine();
			
			var person = JsonConvert.DeserializeObject<Person>(json);
			Console.WriteLine("Deserialized person name...");
			Console.WriteLine(person.Name);

			var Larry2 = new JObject(
				new JProperty("Name", "Larry Nung"),
				new JProperty("NickName", "蹂躪")
				);

			json = JsonConvert.SerializeObject(Larry2, Formatting.Indented);
			Console.WriteLine("Serialized json string...");
			Console.WriteLine(json);
			Console.WriteLine();


			var dotBlog = new
			{
				Blogers = new List<Person>{
					new Person()
					{
						Name = "Larry Nung",
						NickName = "蹂躪"
					},
					new Person()
					{
						Name = "Bill Chung",
						NickName = "Bill uncle"
					}
				}
			};

			var linq = from item in JObject.FromObject(dotBlog)["Blogers"]
					   select item;

			foreach (var item in linq)
			{
				Console.WriteLine(item.ToString());
			}

			StringBuilder sb = new StringBuilder();
			StringWriter sw = new StringWriter(sb);

			using (JsonWriter jsonWriter = new JsonTextWriter(sw))
			{
				jsonWriter.Formatting = Formatting.Indented;

				jsonWriter.WriteStartObject();
				jsonWriter.WritePropertyName("Name");
				jsonWriter.WriteValue("Larry Nung");
				jsonWriter.WritePropertyName("NickName");
				jsonWriter.WriteValue("蹂躪");
				jsonWriter.WriteEndObject();
			}
			Console.WriteLine("Serialized json string...");
			Console.WriteLine(sb.ToString());
			Console.WriteLine();
 
			try
			{
				person = JsonConvert.DeserializeObject<Person>(@"{""Name"": ""Larry Nung"",""NickName"": [蹂躪,拉力]}",new JsonSerializerSettings() 
				{
					Error = (sender, e)=>
					{
						Console.WriteLine("Serialize error!!");
						e.ErrorContext.Handled = true;
					}
				});
			}
			catch 
			{
			}
		}
	}
}

 

運行結果如下:

image

 

Link