Json的序列化與反序列化

来源:http://www.cnblogs.com/lijunfeng-dev/archive/2017/09/18/7538437.html
-Advertisement-
Play Games

想想某一天,你在看LOL攻略的時候,系統突然崩潰了,接著瀏覽器出現了密密麻麻的LOL帳號和密碼,你一定在想:“天啊,這次要發財了,說不定裡面有超凡號或者王者號,我得趕緊全部記下來。”然而說完你就驚呆了,那麼多的帳號密碼,而且全部寫在了Json裡面,一個一個複製粘貼要記到什麼時候啊。。。如果這時候我在 ...


  想想某一天,你在看LOL攻略的時候,系統突然崩潰了,接著瀏覽器出現了密密麻麻的LOL帳號和密碼,你一定在想:“天啊,這次要發財了,說不定裡面有超凡號或者王者號,我得趕緊全部記下來。”然而說完你就驚呆了,那麼多的帳號密碼,而且全部寫在了Json裡面,一個一個複製粘貼要記到什麼時候啊。。。如果這時候我在你身邊,我一定會幫助你的,前提是,要分幾個王者號給我噢。。。

  

 

   言歸正傳。

  上面舉的例子雖然有點不太現實,但其實是想和大家說明一個問題,如果要解析Json或XML,請不要使用檢索字元串的方式!!!

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

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

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

  從上面的定義,我們可以得出兩個結論:

  ① 這兩個過程互為逆過程;

  ② 無論是序列化還是反序列化,對象的狀態信息都與一段序列相對應。

  下麵我們就來看看如何用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直接使用字元串要麻煩不少。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 反射是.NET中的重要機制,通過反射,可以在運行時獲得程式或程式集中每一個類型(包括類、結構、委托、介面和枚舉等)的成員和成員的信息。有了反射,即可對每一個類型瞭如指掌。另外我還可以直接創建對象,即使這個對象的類型在編譯時還不知道。 反射的用途: (1)使用Assembly定義和載入程式集,載入在程 ...
  • 效果圖: C#後臺代碼 ...
  • OAuth 2.0註意事項: 1、 獲取access_token時,請使用POST 1 private static string GetAuthorization(string username, string password) 2 { 3 string authorization = stri ...
  • 泛型(generic)是C#語言2.0和通用語言運行時(CLR)的一個新特性。泛型為.NET框架引入了類型參數(type parameters)的概念。類型參數使得設計類和方法時,不必確定一個或多個具體參數,其的具體參數可延遲到客戶代碼中聲明、實現。這意味著使用泛型的類型參數T,寫一個類MyList ...
  • 這段時間的項目有用到介面,開始不是特別理解介面,只是單單知道介面定義非常簡單,甚至覺得這個介面只是多此一舉(個人開發的時候)。現在開始團隊開發,才發現介面原來是這麼的重要和便捷! 接下來就來談談我這段時間對介面使用的粗淺見解,說的對希望大家贊,說的有誤的地方希望大家多多包涵建議! READY GO! ...
  • 引用ZXing類庫 實現功能: 1生成帶有Logo二維碼 2 將二維碼繪製到圖片上 3 圖片上繪製文字 1 public string CreateQrCode(string md5Str,string name,int sex) 2 { 3 string str = sex == 1? "先生": ...
  • 花了幾個小時寫了一個小程式,沒什麼技術含量,第一次寫博客。本人是個菜鳥,想記錄一下自己的學習。 運行效果如圖: 代碼如下: string url = "https://www.cnblogs.com/"; int pagNum = 10; string html = ""; //string pat ...
  • 1 概述 1 概述 VS2017可以調試JS,本篇文章簡要概述VS2017關於啟用和關閉VS調試功能。 2 具體內容 2 具體內容 當開啟VS2017JS調試功能時,我們用VS2017打開解決方案時,會出現如下界面: 關閉VS2017 js調試功能: 工具=》選項 調試=》去掉"啟用ASP.NET的 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...