Json的序列化與反序列化

  想一想某一天,你在看LOL攻略的時候,系統忽然崩潰了,接着瀏覽器出現了密密麻麻的LOL賬號和密碼,你必定在想:「天啊,此次要發財了,說不定裏面有超凡號或者王者號,我得趕忙所有記下來。」然而說完你就驚呆了,那麼多的賬號密碼,並且所有寫在了Json裏面,一個一個複製粘貼要記到何時啊。。。若是這時候我在你身邊,我必定會幫助你的,前提是,要分幾個王者號給我噢。。。json

  

 

   言歸正傳。數組

  上面舉的例子雖然有點不太現實,但實際上是想和你們說明一個問題,若是要解析Json或XML,請不要使用檢索字符串的方式!!!瀏覽器

       那麼在說明如何解析Json和XML以前,咱們先來搞清楚兩個概念:序列化反序列化工具

      「序列化,即Serialization,是一個將對象的狀態信息轉變爲能夠存儲或傳輸的形式的過程。」性能

    「反序列化,即Deserialization,顧名思義是一個將能夠存儲或傳輸的序列轉變爲某個對象的狀態信息的過程。」學習

  從上面的定義,咱們能夠得出兩個結論:測試

  ① 這兩個過程互爲逆過程;ui

  ② 不管是序列化仍是反序列化,對象的狀態信息都與一段序列相對應。spa

  下面咱們就來看看如何用C#語言進行Json和XML的序列化和反序列化。設計

  

   Json的序列化與反序列化

   ① 使用Newtonsoft.Json庫

  首先咱們先來看一段代碼:

 1 public void JsonDeserialize(string jsonString)
 2 {
 3     //Json的反序列化操做
 4     JObject jObject = JsonConvert.DeserializeObject(jsonString) as JObject;
 5     //清空listBox的結果
 6     listBoxResult.Items.Clear();
 7     //將解析獲得的數據顯示到listBox中
 8     listBoxResult.Items.Add("Name : " + jObject["name"] ?? "null");
 9     listBoxResult.Items.Add("Doing : " + jObject["doing"] ?? "null");
10     listBoxResult.Items.Add("HandleContent : " + jObject["handleContent"] ?? "null");
11     listBoxResult.Items.Add("DateTime : " + jObject["dateTime"] ?? "null");
12 }

  上面這個方法的做用是,接收一段Json序列做爲參數,在完成反序列化後,將全部字段的值輸出到ListBox中,下面是簡單的測試:

  

  能夠看到,僅僅用了一行代碼,Json的內容就按照字段全都被獲取到了,下面就來好好告終一下Newtonsoft.Json這個庫的用法吧。

  Newtonsoft.Json是.NET平臺中一個很是流行且高性能的Json序列化和反序列化開源庫,提供了許多用於序列化和反序列化Json的類及方法,除此以外它還提供了用Linq操做Json、將Json轉換成XML的功能,相較於傳統的用於序列化和反序列化的DataContractJsonSerializer類更好用,更高效。本文只介紹其中比較重要的JsonConvert類和幾個經常使用的類型,更多內容你們能夠訪問Json.NET的主頁進行了解。

  在介紹使用方法前,先來講說Newtonsoft.Json.dll的引入。從VS2013開始,ASP.NET的項目均自帶了Newtonsoft.Json.dll,直接使用便可,而其餘版本或者其餘項目若是沒有的話,能夠進入其官方主頁,找到Github託管的地址進入並下載。下面演示引用該動態連接庫的流程,

  ① 點擊菜單欄的項目->添加引用,或者在解決方案資源管理器中找到項目,右鍵引用屬性,點擊添加引用,而後就會打開該窗口。

  該窗口打開後,若是是第一次添加Newtonsoft.Json.dll,請找到右下方的瀏覽按鈕,並選擇Newtonsoft.Json.dll;若之前添加過該庫,能夠點擊左側的瀏覽菜單,並點擊最近,右側會顯示之前添加過的庫的信息,勾上須要的庫,最後點擊右下方的肯定便可。

  

  

  添加了動態連接庫還沒完,要想使用其中的類和方法,須要引入相應的命名空間(若是你不想每定義一個對象就寫一長串命名空間的話),本文主要用到的命名空間是Newtonsoft.Json以及Newtonsoft.Json.Linq

  

  完成這兩步以後就能夠舒舒服服的開始解析咱們偉大的Json了。

  Now,先來介紹本庫比較重要的類,JsonConvert。JsonConvert是個很是實用的工具類,它提供了用於反序列化的DeserializeObject方法和DeserializeObject<T>方法,以及用於序列化的SerializeObject方法。因爲這些方法都是靜態方法,所以無需建立JsonConvert對象,直接使用妥妥的(事實上JsonConvert是個靜態類,根本沒法被實例化)。

  在最開始演示的例子中,咱們使用了JsonConvert.DeserializeObject方法,這是最原始的Json反序列化方法,它接收string類型的參數並返回object類型的對象。那麼你也許會問到,它返回的對象到底是什麼類型???想要真正得到該對象的信息,就必須將其強制轉換成具體的派生類。那麼,咱們可使用GetType方法來一探究竟。

1 private void btnDeserialize_Click(object sender, EventArgs e)
2 {
3     //JsonDeserialize(textBox1.Text);
4     var secretObject = JsonConvert.DeserializeObject(textBox1.Text);
5     MessageBox.Show(secretObject.GetType().ToString());
6 }

   咱們先在一個按鈕的點擊事件對應的方法中解析輸入的Json序列,因爲咱們還不肯定JsonConvert.DeserializeObject方法返回什麼對象,所以咱們先用隱式類型對象去引用該方法返回的結果,而後調用其GetType方法,獲取該對象所屬類型的元數據,最後將對象的完整類型名顯示在對話框當中。執行這段代碼,你將會看到以下結果:

  

  也許這時候在你的內心又有一萬個草泥馬呼嘯而過,「What?還沒搞清楚JsonConvert類的用法,怎麼又跑出了一個奇怪的類?」

  可是不要緊,接下來我會對你們的謎團進行逐一的解釋。

   在Newtonsoft.Json.Linq命名空間中,定義了一些用於表示不一樣形式的Json字符串的類,它們包括JObject、JProperty、JToken、JArray、JValue、JContainer等等。

        那麼在這些類當中,JToken充當着標杆的角色,它是一個抽象類,並實現了IJEnumerable<T>接口,而IJEnumerable<T>接口繼承自IEnumerable<T>接口,這意味着全部繼承JToken的類均可以使用foreach語句進行遍歷,而接下來要介紹的這些類,全都派生自JToken。另外,JToken還定義了First、Last、Parent、Root、Item、Previous、Next等遍歷常常會用到的屬性,所以在使用這些類的時候,遍歷、查找具體類等操做就顯得很是的簡單。說了這麼多,是時候說說JObject了,這是個JToken的派生類,同時也是實現了JContainer接口、ICollection<T>、IKeyValuePair<T>,這意味着JObject能夠存儲多個繼承JToken類型的對象,同時也能夠遍歷它們,還能夠用鍵去獲取相應的值,也許你還不清楚這些有什麼用,那麼咱們先來了解一下Json的基本結構:

  通常來講,被大括號括起來的內容能夠當作是一個總體的(一個對象的),而被中括號括起來的內容,能夠當作是一組內容(一個數組),在一個複雜的Json結構中,大括號裏面的某個鍵的值能夠在嵌一個大括號,表明該屬性值爲另外一個Json對象,同理一個大括號中的某個鍵的值能夠嵌一箇中括號,表明該屬性值是一個數組,如

 1 //對於這種Json來講,能夠當作是一個對象,裏面包含了name、doing、handleContent和dateTime屬性。
 2 {
 3     "name":".NET Coder",   
 4     "doing":"Deserialization",
 5     "handleContent":"Json",
 6     "dateTime":"2017-09-11 12:12:12"
 7 }
 8 
 9 //對於這種Json來講,能夠當作是一個數組對象,裏面包含了兩個對象。
10 [
11   {
12     "name":".NET Coder",   
13     "doing":"Deserialization",
14     "handleContent":"Json",
15     "dateTime":"2017-09-11 12:12:12"
16   },
17   {
18     "name":"Java Coder",   
19     "doing":"Deserialization",
20     "handleContent":"Json",
21     "dateTime":"2017-09-22 21:21:21"
22   }
23 ]

  在上一個演示的例子中,咱們用GetType方法去判斷完成反序列化以後,實際返回的類型是什麼;那麼接下來咱們就將上面這兩種不一樣結構的Json序列放入程序中解析,看看獲得的對象是否是都是JObject。

  

      成功獲得了JObject類型的對象。

  

  此次咱們居然獲得了JArray?!

  這下咱們也許就明白了,JObject用於表示一個完整的Json對象結構,而JArray用於表示一個Json數組,其中會包括一個或多個Json對象。

  在得到JObject對象以後,咱們能夠用屬性名(鍵)去得到相應的屬性值,也可使用Next、Previous等屬性去逐個訪問JObject的屬性。

  而得到JArray對象以後,咱們可使用for或者foreach語句去遍歷該對象內的元素,而後根據某個屬性名或Next、Previous等屬性去訪問當前元素某個屬性的屬性值。

  那麼JProperty和JValue類型又有什麼做用呢?

  咱們能夠來調試一下程序,並觀察JObject對象內的結構,

  如圖咱們在使用foreach循環遍歷JObject內部的屬性,能夠看到,First屬性指向了JObject的第一個屬性,而它的類型是JProperty,由此咱們能夠知道,JProperty類用於描述一個Json對象中的某一個屬性,在其內部包含了一個KeyValuePair<string, JToken>的成員變量,用於存儲該Json屬性的鍵值對,而這個值,其實就是JValue類型的變量。

  

  那麼到這裏爲止,咱們已經掌握了DeserializeObject方法的用法了,可是很明顯,這種方法解析Json數據實在太麻煩了,咱們須要把全部Json對象的屬性一個個獲取到,而後才能獲得其中的值,有沒有更簡介的辦法呢?

  有!有!有!重要的事情說三遍。

  下面該輪到DeserializeObject<T>方法出場了,這是個泛型方法,指定T的類型並傳入Json字符串做爲參數後,該方法會完成反序列化並返回一個T類型的對象。那麼咱們該指定什麼類型給該方法呢?這個類型須要根據Json數據的層次關係自行設計,若是你們是第一次使用,不知道該如何設計,那麼咱們來讓VS幫咱們設計,下面給你們介紹一個神奇的功能,VS的自動建類功能:

  仍是這段Json序列,假設咱們須要將其反序列化,如今殊不知道怎麼設計類,那麼咱們能夠先複製這段Json序列,

1 {
2     "name":".NET Coder",   
3     "doing":"Serialization",
4     "handleContent":"Json",
5     "dateTime":"2017-09-11 12:12:12"
6 }

  打開VS,在菜單欄找到編輯->選擇性粘貼,點擊將Json粘貼爲類,就會出現下面的結果,

  本來空白的地方,忽然出現了一個類的定義(若是Json有多個層級,VS會幫咱們生成多個類),這個類清晰的反映了這段Json的結構,此時咱們只須要將這個自動生成的類,用到方法裏就行了:

  

   

  咱們改寫了JsonDeserialize方法,並使用了由VS生成的類,能夠看到咱們不須要再根據屬性名去得到屬性值了,也不用再擔憂忘記有什麼屬性或者屬性名寫錯,只須要在對象後面加上一點,該對象的全部屬性都會出如今選擇列表中。是否是忽然以爲眼前一亮,一身輕鬆了呢?但其實這個作法隱藏了一點點小問題,由於VS是根據Json原來的屬性名來定義屬性的,這就要求Json的屬性名遵循C#的標識符定義規則,而後事實上,在Json中可能會出現不符合C#標識符定義規範的屬性名,這並無錯,以下面這段Json:

1 {
2     "name":"money",   
3     "price":"66.6",
4     "int":"64",
5     "a-b":"c-d",
6     "@#$":"sdfsdf"
7 }

  能夠看到,雖然VS會用_去代替不合法的字符串並按照規範生成屬性名,可是這樣子的屬性名已經失去了它的意義,咱們沒法得知這個屬性表達了什麼含義,爲了解決這個問題,咱們可使用C#的註解來給該類的屬性制定數據協定

  

 

   DataContract和DataMember註解來自於命名空間System.Runtime.Serialization,這個命名空間所在的動態連接庫項目默認是沒有引入的,須要手動引入,咱們只須要按照上面引入Newtonsoft.Json.dll的方式引入就好,可是須要注意的是,System.Runtime.Serialization所在的動態連接庫在VS程序集中是提供了的,咱們只須要在添加引用界面,點擊程序集,並在右側的列表找到System.Runtime.Serialization並勾選,而後點擊右下角的肯定按鈕便可。

  完成這些步驟以後咱們就能夠改造Json的實體類,爲其添加數據協定。什麼是數據協定呢,即數據的發送方和接收方不須要使用相同的類型,只須要遵循數據協定便可。爲了使該類可以添加數據協定,須要在類名上方添加[DataContract]註解,說明該類的屬性使用了數據協定,而且容許將該類進行序列化,若是不加這個註解,就不能使用[DataMember]註解了。添加[DataContract]註解後,咱們就能夠在對應的屬性上添加[DataMember]註解,該註解有4個參數:

參數名 做用
Name 標識該屬性對應的Json屬性名
Order 標識該屬性序列化和反序列化的順序
IsRequired 標識該屬性在讀取或反序列化時是否必須存在,類型爲bool
EmitDefaultValue 標識在序列化的過程當中是否使用該屬性的默認值來序列化

 

  上圖咱們使用了Name參數來設置每一個屬性對應的Json屬性名,從而加強了該類在程序中的可讀性,同時又不會對序列化和反序列化的過程產生麻煩。下面再改寫一下JsonDeserialize方法吧:

  

   

  測試成功!!!

   另外再補充一個小知識,當咱們須要反序列化不少簡單的Json時,爲了不建立過多的實體類,咱們可使用dynamic動態類型來代替自定義的實體類,在這裏咱們改寫上述例子,使用dynamic類型來實現相應功能:

  

 

  在這個例子中,我沒有定義新的類,而是使用了動態類型,這種類型的對象有一個特色:運行時檢查。在使用這種對象的時候咱們要注意,不管是使用它的屬性,仍是調用它的方法,編譯器都不會對其進行編譯時檢查,也就是說咱們必須保證屬性名或者方法名沒有寫錯,不然會出現表達式爲空或者引起異常的現象。

  

   

  調用不存在的屬性返回Null。

 

  

  調用不存在的方法引起異常。

 

  

  在沒有錯誤的狀況下,成功運行!

  

  到這裏反序列化的介紹算是告一段落了,接下來咱們講講使用JsonConvert.SerializeObject方法完成序列化操做。

  前面那麼多操做的目的是爲了解析Json字符串,讓咱們能方便的得到其中的信息,可是若是咱們不是信息的接收方而是發送方呢,咱們要怎麼構造Json字符串?顯然不可能用字符串拼接的方法,效率低下,難以排錯。所以今天咱們就要用一行代碼來完成這件事情。

  

  

 

  沒錯,就只有一行代碼!!!

 

  

  

  看吧,是否是一行代碼就讓一個活生生的對象瞬間變成了一串Json了呢!!!

  

  ② 使用DataContractJsonSerializer類

  其實本人並不想講解這個類,第一使用過程很是麻煩,第二效率低下,實際工做中也不會用它,可是從學習的角度咱們仍是要了解一下這個類的存在,而且這個類序列化和反序列化Json的過程和XML的序列化和反序列化很類似,所以仍是有必要讓你們瞭解一下。

  在此很少說其餘廢話,直接上代碼:

  DataContractJsonSerializer序列化

 1 public void OldJsonSerialize()
 2 {
 3     DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(HandleObject));
 4     //建立實體類
 5     HandleObject jObject = new HandleObject() {
 6         Name = ".NET Coder",
 7         Doing = "Serialization",
 8         HandleContent = "Json",
 9         DateTime = DateTime.Now.ToString("yyyy-MM-ss")
10     };
11     //建立內存流,用於存儲序列化後獲得的內容
12     MemoryStream ms = new MemoryStream();
13     //將對象序列化後存入內存流
14     serializer.WriteObject(ms, jObject);
15     //將內存流的內容轉換成字符串並輸出到文本框
16     textBox1.Text = Encoding.UTF8.GetString(ms.ToArray());
17 }

 

  DataContractJsonSerializer反序列化

 1 public void OldJsonDeserialize()
 2 {
 3     DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(HandleObject));
 4     //將Json字符串轉換成字節數組
 5     byte[] content = Encoding.UTF8.GetBytes(textBox1.Text);
 6     //用字節數組初始化一個內存流對象
 7     MemoryStream ms = new MemoryStream(content);
 8     //完成反序列化操做
 9     HandleObject handleObject = serializer.ReadObject(ms) as HandleObject;
10     //將解析獲得的數據顯示到listBox中
11     listBoxResult.Items.Add("Name : " + handleObject.Name ?? "null");
12     listBoxResult.Items.Add("Doing : " + handleObject.Doing ?? "null");
13     listBoxResult.Items.Add("HandleContent : " + handleObject.HandleContent ?? "null");
14     listBoxResult.Items.Add("DateTime : " + (handleObject.DateTime.ToString() ?? "null"));
15 }

 

  DataContractJsonSerializer類和JsonConvert類最大的不一樣就在於,DataContractJsonSerializer類將操做流的細節暴露了出來,須要由用戶自行完成,相比JsonConvert直接使用字符串要麻煩很多。

相關文章
相關標籤/搜索