1.什么是JSON
JSON(JavaScript Object Notation) 是一種輕量級的數據交換格式。它基于JavaScript的一個子集。 JSON采用完全獨立于語言的文本格式,但是也使用了類似于C語言家族的習慣(包括C, C++, C#, Java, JavaScript, Perl, Python等)。這些特性使JSON成為理想的數據交換語言。易于人閱讀和編寫,同時也易于機器解析和生成。
2.JSON語法規則
JSON 語法是 JavaScript 對象表示法語法的子集。
l? 數據在名稱/值對中
l? 數據由逗號分隔
l? 花括號保存對象
l? 方括號保存數組
JSON 數據的書寫格式是:名稱/值對。
名稱/值對包括字段名稱(在雙引號中),后面寫一個冒號,然后是值:
"firstName" : "John"
這很容易理解,等價于這條 JavaScript 語句:
firstName = "John"
JSON 值可以是:
l? 數字(整數或浮點數)
l? 字符串(在雙引號中)
l? 邏輯值(true 或 false)
l? 數組(在方括號中)
l? 對象(在花括號中)
l? null
JSON在線校驗格式化工具:bejson
3.JSON基礎結構
JSON建構有兩種結構
JSON簡單說就是javascript中的對象和數組,所以這兩種結構就是對象和數組兩種結構,通過這兩種結構可以表示各種復雜的結構。
1、對象:對象在js中表示為“{}”括起來的內容,數據結構為 {key:value,key:value,...}的鍵值對的結構,在面向對象的語言中,key為對象的屬性,value為對應的屬性值,所以很容易理解,取值方法為 對象.key 獲取屬性值,這個屬性值的類型可以是 數字、字符串、數組、對象幾種。
2、數組:數組在js中是中括號“[]”括起來的內容,數據結構為 ["java","javascript","vb",...],取值方式和所有語言中一樣,使用索引獲取,字段值的類型可以是 數字、字符串、數組、對象幾種。
經過對象、數組2種結構就可以組合成復雜的數據結構了。
4.JSON基礎示例
簡單地說,JSON 可以將 JavaScript 對象中表示的一組數據轉換為字符串,然后就可以在函數之間輕松地傳遞這個字符串,或者在異步應用程序中將字符串從 Web 客戶機傳遞給服務器端程序。這個字符串看起來有點兒古怪,但是JavaScript很容易解釋它,而且 JSON 可以表示比"名稱 / 值對"更復雜的結構。例如,可以表示數組和復雜的對象,而不僅僅是鍵和值的簡單列表。
名稱 / 值對
按照最簡單的形式,可以用下面這樣的 JSON 表示"名稱 / 值對":
{ "firstName": "Brett" }
這個示例非常基本,而且實際上比等效的純文本"名稱 / 值對"占用更多的空間:
firstName=Brett
但是,當將多個"名稱 / 值對"串在一起時,JSON 就會體現出它的價值了。首先,可以創建包含多個"名稱 / 值對"的 記錄,比如:
{ "firstName": "Brett", "lastName":"McLaughlin", "email": "aaaa" }
從語法方面來看,這與"名稱 / 值對"相比并沒有很大的優勢,但是在這種情況下 JSON 更容易使用,而且可讀性更好。例如,它明確地表示以上三個值都是同一記錄的一部分;花括號使這些值有了某種聯系。
表示數組
當需要表示一組值時,JSON 不但能夠提高可讀性,而且可以減少復雜性。例如,假設您希望表示一個人名列表。在XML中,需要許多開始標記和結束標記;如果使用典型的名稱 / 值對(就像在本系列前面文章中看到的那種名稱 / 值對),那么必須建立一種專有的數據格式,或者將鍵名稱修改為 person1-firstName這樣的形式。
如果使用 JSON,就只需將多個帶花括號的記錄分組在一起:
1 2 3 4 5 6 7 | { "people" : [ ???????????????? { "firstName" : "Brett" , "lastName" : "McLaughlin" , "email" : "aaaa" }, ???????????????? { "firstName" : "Jason" , "lastName" : "Hunter" , "email" : "bbbb" }, ???????????????? { "firstName" : "Elliotte" , "lastName" : "Harold" , "email" : "cccc" } ???????????? ] } |
這不難理解。在這個示例中,只有一個名為 people的變量,值是包含三個條目的數組,每個條目是一個人的記錄,其中包含名、姓和電子郵件地址。上面的示例演示如何用括號將記錄組合成一個值。當然,可以使用相同的語法表示多個值(每個值包含多個記錄):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | { "programmers" : [ { "firstName" : "Brett" , "lastName" : "McLaughlin" , "email" : "aaaa" }, { "firstName" : "Jason" , "lastName" : "Hunter" , "email" : "bbbb" }, { "firstName" : "Elliotte" , "lastName" : "Harold" , "email" : "cccc" } ], "authors" : [ { "firstName" : "Isaac" , "lastName" : "Asimov" , "genre" : "science fiction" }, { "firstName" : "Tad" , "lastName" : "Williams" , "genre" : "fantasy" }, { "firstName" : "Frank" , "lastName" : "Peretti" , "genre" : "christian fiction" } ], "musicians" : [ { "firstName" : "Eric" , "lastName" : "Clapton" , "instrument" : "guitar" }, { "firstName" : "Sergei" , "lastName" : "Rachmaninoff" , "instrument" : "piano" } ] } |
這里最值得注意的是,能夠表示多個值,每個值進而包含多個值。但是還應該注意,在不同的主條目(programmers、authors 和 musicians)之間,記錄中實際的名稱 / 值對可以不一樣。JSON 是完全動態的,允許在 JSON 結構的中間改變表示數據的方式。
在處理 JSON 格式的數據時,沒有需要遵守的預定義的約束。所以,在同樣的數據結構中,可以改變表示數據的方式,甚至可以以不同方式表示同一事物。
5.JSON和XML比較
可讀性
JSON和XML的可讀性可謂不相上下,一邊是簡易的語法,一邊是規范的標簽形式,很難分出勝負。
可擴展性
XML天生有很好的擴展性,JSON當然也有,沒有什么是XML能擴展,而JSON卻不能擴展的。不過JSON在Javascript主場作戰,可以存儲Javascript復合對象,有著xml不可比擬的優勢。
編碼難度、解碼難度(略)
實例比較
XML和JSON都使用結構化方法來標記數據,下面來做一個簡單的比較。
用XML表示中國部分省市數據如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | <? xml version = "1.0" encoding = "utf-8" ?> < country > ???? < name >中國</ name > ???? < province > ???????? < name >黑龍江</ name > ???????? < cities > ???????????? < city >哈爾濱</ city > ???????????? < city >大慶</ city > ???????? </ cities > ???? </ province > ???? < province > ???????? < name >廣東</ name > ???????? < cities > ???????????? < city >廣州</ city > ???????????? < city >深圳</ city > ???????????? < city >珠海</ city > ???????? </ cities > ???? </ province > ???? < province > ???????? < name >臺灣</ name > ???????? < cities > ???????????? < city >臺北</ city > ???????????? < city >高雄</ city > ???????? </ cities > ???? </ province > ???? < province > ???????? < name >新疆</ name > ???????? < cities > ???????????? < city >烏魯木齊</ city > ???????? </ cities > ???? </ province > </ country > |
用JSON表示如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | { ???? "name" : "中國" , ???? "province" :[ ???? { ??????? "name" : "黑龍江" , ???????? "cities" :{ ???????????? "city" :[ "哈爾濱" , "大慶" ] ???????? } ????? }, ????? { ???????? "name" : "廣東" , ???????? "cities" :{ ???????????? "city" :[ "廣州" , "深圳" , "珠海" ] ???????? } ???? }, ???? { ???????? "name" : "臺灣" , ???????? "cities" :{ ???????????? "city" :[ "臺北" , "高雄" ] ???????? } ???? }, ???? { ???????? "name" : "新疆" , ???????? "cities" :{ ???????????? "city" :[ "烏魯木齊" ] ???????? } ???? } ] } |
6. .NET操作JSON
JSON文件讀入到內存中就是字符串,.NET操作JSON就是生成與解析JSON字符串。操作JSON通常有以下幾種方式:
1. 原始方式:自己按照JSON的語法格式,寫代碼直接操作JSON字符串。如非必要,應該很少人會走這條路,從頭再來的。
2. 通用方式:這種方式是使用開源的類庫Newtonsoft.Json(下載地址http://json.codeplex.com/)。下載后加入工程就能用。通常可以使用JObject, JsonReader, JsonWriter處理。這種方式最通用,也最靈活,可以隨時修改不爽的地方。
(1)使用JsonReader讀Json字符串:
1 2 3 4 5 6 | string jsonText = @"{""input"" : ""value"", ""output"" : ""result""}" ; JsonReader reader = new JsonTextReader( new StringReader(jsonText)); while (reader.Read()) { ???? Console.WriteLine(reader.TokenType + "\t\t" + reader.ValueType + "\t\t" + reader.Value); } |
(2)使用JsonWriter寫字符串:
1 2 3 4 5 6 7 8 9 10 11 | StringWriter sw = new StringWriter(); JsonWriter writer = new JsonTextWriter(sw); writer.WriteStartObject(); writer.WritePropertyName( "input" ); writer.WriteValue( "value" ); writer.WritePropertyName( "output" ); writer.WriteValue( "result" ); writer.WriteEndObject(); writer.Flush(); string jsonText = sw.GetStringBuilder().ToString(); Console.WriteLine(jsonText); |
(3)使用JObject讀寫字符串:
1 2 | JObject jo = JObject.Parse(jsonText); string [] values = jo.Properties().Select(item => item.Value.ToString()).ToArray(); |
(4)使用JsonSerializer讀寫對象(基于JsonWriter與JsonReader):
1 2 3 4 5 6 7 8 | Project p = new Project() { Input = "stone" , Output = "gold" }; JsonSerializer serializer = new JsonSerializer(); StringWriter sw = new StringWriter(); serializer.Serialize( new JsonTextWriter(sw), p); Console.WriteLine(sw.GetStringBuilder().ToString()); StringReader sr = new StringReader( @"{""Input"":""stone"", ""Output"":""gold""}" ); Project p1 = (Project)serializer.Deserialize( new JsonTextReader(sr), typeof (Project)); Console.WriteLine(p1.Input + "=>" + p1.Output); |
上面的代碼都是基于下面這個Project類定義:
1 2 3 4 5 | class Project { ???? public string Input { get ; set ; } ???? public string Output { get ; set ; } } |
此外,如果上面的JsonTextReader等類編譯不過的話,說明是我們自己修改過的類,換成你們自己的相關類就可以了,不影響使用。
3. 內置方式:使用.NET Framework 3.5/4.0中提供的System.Web.Script.Serialization命名空間下的JavaScriptSerializer類進行對象的序列化與反序列化,很直接。
1 2 3 4 5 6 7 | Project p = new Project() { Input = "stone" , Output = "gold" }; JavaScriptSerializer serializer = new JavaScriptSerializer(); var json = serializer.Serialize(p); Console.WriteLine(json); var p1 = serializer.Deserialize<Project>(json); Console.WriteLine(p1.Input + "=>" + p1.Output); Console.WriteLine(ReferenceEquals(p,p1)); |
注意:如果使用的是VS2010,則要求當前的工程的Target Framework要改成.Net Framework 4,不能使用Client Profile。當然這個System.Web.Extensions.dll主要是Web使用的,直接在Console工程中用感覺有點浪費資源。
此外,從最后一句也可以看到,序列化與反序列化是深拷貝的一種典型的實現方式。
更新1:
注意用System.Web.Script.Serialization的時候,序列化沒問題,反序列化會將DateTime賦值成了UTC時間。
UTC時間 + 時區差 = 本地時間
6:02 + (+0800) = 14:02(北京時間)
1 2 3 4 5 6 7 | DateTime dt = DateTime.Now; JavaScriptSerializer serializer = new JavaScriptSerializer(); var json = serializer.Serialize(dt); Console.WriteLine(json); var dt1 = serializer.Deserialize<DateTime>(json); Response.Write(dt1.ToString()+ "<br />" ); Response.Write(ReferenceEquals(dt, dt1)); |
? 關于UTC時間與北京時間的區別請移步:UTC時間與北京時間差多久?
4. 契約方式:使用System.Runtime.Serialization.dll提供的DataContractJsonSerializer或者 JsonReaderWriterFactory實現。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | Project p = new Project() { Input = "stone" , Output = "gold" }; DataContractJsonSerializer serializer = new DataContractJsonSerializer(p.GetType()); string jsonText; ? ?using (MemoryStream stream = new MemoryStream()) { ???? serializer.WriteObject(stream, p); ???? jsonText = Encoding.UTF8.GetString(stream.ToArray()); ???? Console.WriteLine(jsonText); } using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(jsonText))) { ???? DataContractJsonSerializer serializer1 = new DataContractJsonSerializer( typeof (Project)); ???? Project p1 = (Project)serializer1.ReadObject(ms); ???? Console.WriteLine(p1.Input + "=>" + p1.Output); } |
這里要注意,這里的Project類和成員要加相關的Attribute:
1 2 3 4 5 6 7 8 | [DataContract] class Project { ???? [DataMember] ???? public string Input { get ; set ; } ???? [DataMember] ???? public string Output { get ; set ; } } |
實用參考:
JSON驗證工具:http://jsonlint.com/
JSON簡明教程:http://www.w3school.com.cn/json/
Newtonsoft.Json類庫下載:http://json.codeplex.com/
通過序列化將.net對象轉換為JSON字符串
在web開發過程中,我們經常需要將從數據庫中查詢到的數據(一般為一個集合,列表或數組等)轉換為JSON格式字符串傳回客戶端,這就需要進行序列化,這里用到的是JsonConvert對象的SerializeObject方法。其語法格式為:JsonConvert.SerializeObject(object),代碼中的”object”就是要序列化的.net對象,序列化后返回的是json字符串。
比如,現在我們有一個TStudent的學生表,表中的字段和已有數據如圖所示
?
從表中我們可以看到一共有五條數據,現在我們要從數據庫中取出這些數據,然后利用JSON.NET的JsonConvert對象序列化它們為json字符串,并顯示在頁面上。C#代碼如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | protected void Page_Load( object sender, EventArgs e) ???????? { ???????????? using (L2SDBDataContext db = new L2SDBDataContext()) ???????????? { ???????????????? List<Student> studentList = new List<Student>(); ???????????????? var query = from s in db.TStudents ???????????????????????????? select new { ???????????????????????????????? StudentID=s.StudentID, ???????????????????????????????? Name=s.Name, ???????????????????????????????? Hometown=s.Hometown, ???????????????????????????????? Gender=s.Gender, ???????????????????????????????? Brithday=s.Birthday, ???????????????????????????????? ClassID=s.ClassID, ???????????????????????????????? Weight=s.Weight, ???????????????????????????????? Height=s.Height, ???????????????????????????????? Desc=s.Desc ???????????????????????????? }; ???????????????? foreach (var item in query) ???????????????? { ???????????????????? Student student = new Student { StudentID=item.StudentID,Name=item.Name,Hometown=item.Hometown,Gender=item.Gender,Brithday=item.Brithday,ClassID=item.ClassID,Weight=item.Weight,Height=item.Height,Desc=item.Desc}; ???????????????????? studentList.Add(student); ???????????????? } ???????????????? lbMsg.InnerText = JsonConvert.SerializeObject(studentList); ???????????? } ???????? } |
輸出結果:
從圖中我們可以看到,數據庫中的5條記錄全部取出來并轉化為json字符串了。
使用LINQ to JSON定制JSON數據
使用JsonConvert對象的SerializeObject只是簡單地將一個list或集合轉換為json字符串。但是,有的時候我們的前端框架比如ExtJs對服務端返回的數據格式是有一定要求的,比如下面的數據格式,這時就需要用到JSON.NET的LINQ to JSON,LINQ to JSON的作用就是根據需要的格式來定制json數據。
比如經常用在分頁的json格式如代碼:
1 2 3 4 5 6 | { ???? "total" : 5, //記錄總數 ???? "rows" :[ ???????? //json格式的數據列表 ???? ] } |
使用LINQ to JSON前,需要引用Newtonsoft.Json的dll和using Newtonsoft.Json.Linq的命名空間。LINQ to JSON主要使用到JObject, JArray, JProperty和JValue這四個對象,JObject用來生成一個JSON對象,簡單來說就是生成”{}”,JArray用來生成一個JSON數組,也就是”[]”,JProperty用來生成一個JSON數據,格式為key/value的值,而JValue則直接生成一個JSON值。下面我們就用LINQ to JSON返回上面分頁格式的數據。代碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | protected void Page_Load( object sender, EventArgs e) ???????? { ???????????? using (L2SDBDataContext db = new L2SDBDataContext()) ???????????? { ???????????????? //從數據庫中取出數據并放到列表list中 ???????????????? List<Student> studentList = new List<Student>(); ???????????????? var query = from s in db.TStudents ???????????????????????????? select new ???????????????????????????? { ???????????????????????????????? StudentID = s.StudentID, ???????????????????????????????? Name = s.Name, ???????????????????????????????? Hometown = s.Hometown, ???????????????????????????????? Gender = s.Gender, ???????????????????????????????? Brithday = s.Birthday, ???????????????????????????????? ClassID = s.ClassID, ???????????????????????????????? Weight = s.Weight, ???????????????????????????????? Height = s.Height, ???????????????????????????????? Desc = s.Desc ???????????????????????????? }; ???????????????? foreach (var item in query) ???????????????? { ???????????????????? Student student = new Student { StudentID = item.StudentID, Name = item.Name, Hometown = item.Hometown, Gender = item.Gender, Brithday = item.Brithday, ClassID = item.ClassID, Weight = item.Weight, Height = item.Height, Desc = item.Desc }; ???????????????????? studentList.Add(student); ???????????????? } ? ????????????????? //基于創建的list使用LINQ to JSON創建期望格式的JSON數據 ???????????????? lbMsg.InnerText = new JObject( ???????????????????????? new JProperty( "total" ,studentList.Count), ???????????????????????? new JProperty( "rows" , ???????????????????????????????? new JArray( ???????????????????????????????????????? //使用LINQ to JSON可直接在select語句中生成JSON數據對象,無須其它轉換過程 ???????????????????????????????????????? from p in studentList ???????????????????????????????????????? select new JObject( ???????????????????????????????????????????????? new JProperty( "studentID" ,p.StudentID), ???????????????????????????????????????????????? new JProperty( "name" ,p.Name), ???????????????????????????????????????????????? new JProperty( "homeTown" ,p.Hometown) ???????????????????????????????????????????? ) ???????????????????????????????????? ) ???????????????????????????? ) ???????????????????? ).ToString(); ???????????? } ???????? } |
輸出結果為:
處理客戶端提交的JSON數據
客戶端提交過來的數據一般都是json字符串,有了更好地進行操作(面向對象的方式),所以我們一般都會想辦法將json字符串轉換為json對象。例如客戶端提交了以下數組格式json字符串。
1 2 3 4 5 | [ ???? {StudentID: "100" ,Name: "aaa" ,Hometown: "china" }, ???? {StudentID: "101" ,Name: "bbb" ,Hometown: "us" }, ???? {StudentID: "102" ,Name: "ccc" ,Hometown: "england" } ] |
在服務端就可以使用JObject或JArray的Parse方法輕松地將json字符串轉換為json對象,然后通過對象的方式提取數據。下面是服務端代碼。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | protected void Page_Load( object sender, EventArgs e) ???????? { ???????????? string inputJsonString = @" ???????????????? [ ???????????????????? {StudentID:'100',Name:'aaa',Hometown:'china'}, ???????????????????? {StudentID:'101',Name:'bbb',Hometown:'us'}, ???????????????????? {StudentID:'102',Name:'ccc',Hometown:'england'} ???????????????? ]" ; ???????????? JArray jsonObj = JArray.Parse(inputJsonString); ???????????? string message = @"<table border='1'> ???????????????????? <tr><td width='80'>StudentID</td><td width='100'>Name</td><td width='100'>Hometown</td></tr>" ; ???????????? string tpl = "<tr><td>{0}</td><td>{1}</td><td>{2}</td></tr>" ; ???????????? foreach (JObject jObject in jsonObj) ???????????? { ???????????????? message += String.Format(tpl, jObject[ "StudentID" ], jObject[ "Name" ],jObject[ "Hometown" ]); ???????????? } ???????????? message += "</table>" ; ???????????? lbMsg.InnerHtml = message; ???????? } |
輸出結果:
當然,服務端除了使用LINQ to JSON來轉換json字符串外,也可以使用JsonConvert的DeserializeObject方法。如下面代碼實現上面同樣的功能。
1 2 3 4 5 | List<Student> studentList = JsonConvert.DeserializeObject<List<Student>>(inputJsonString); //注意這里必須為List<Student>類型,因為客戶端提交的是一個數組json ???????????? foreach (Student student in studentList) ???????????? { ???????????????? message += String.Format(tpl, student.StudentID, student.Name,student.Hometown); ???????????? } |
總結:
在客戶端,讀寫json對象可以使用”.”操作符或”["key”]”,json字符串轉換為json對象使用eval()函數。
在服務端,由.net對象轉換json字符串優先使用JsonConvert對象的SerializeObject方法,定制輸出json字符串使用LINQ to JSON。由json字符串轉換為.net對象優先使用JsonConvert對象的DeserializeObject方法,然后也可以使用LINQ to JSON。