從XML到JSON
當下應用開發常見的B/S架構之下,我們會遇到很多需要進行前後端資料傳輸的場景。而在這個傳輸的過程中,資料通過何種格式傳輸、方式是否迅速便捷、書寫方式是否簡單易學,都成為了程式設計師在開發時要考量的問題。
在1996年,W3C(World Wide Web Consortium,全球資訊網聯盟)正式公佈了XML1.0標準,
XML採用標準格式為基於Web的應用提供了一個統一進行資料描述和資料交換的標準,不同於HTML側重於解決":如何將檔案顯示在瀏覽器中",XML更加側重於解決:"如何將資料以結構化方式描述"。
(需要注意的是,XML並不是一種程式語言,而是一種跨語言的資料格式。)
XML本身並不複雜,但是加上W3C制定的DTD、XSD、XPath、XSLT等二十多個標準之後,這個簡單的資料交換格式平白變得複雜了起來。程式設計師但凡遇到,只能頭大。苦心孤詣研究大半個月,也不好輕言自己全部清楚了。
而此時,推動著技術前進的另一臺蒸汽機也被點燃——Ajax技術開始流行,映襯出XML越來越不容忽視的缺點。XML得以實現是基於DOM樹,而DOM在各種瀏覽器中的實現細節不盡相同,所以XML的跨瀏覽器相容性並不好,這時需要一種新的資料負載格式整合到HTML頁面中,以滿足Ajax的要求。
終於,在XML誕生後的第八年——2002年,由Douglas Crockford開始使用JSON這種輕量級資料交換格式。
首條JSON資訊發出後,最讓人們驚訝的是,這並不是一個全新的資料格式,它就是JavaScript。
document.domain = 'fudco';parent.session.receive( { to: "session", do: "test", text: "Hello world" } )
而由於這條資料內容本身就是JavaScript,因此不再需要做任何額外解析,使用JS編譯器就可以解決一切。
由於JSON非常簡單,很快就風靡Web世界,並且成為ECMA標準。幾乎所有程式語言都有解析JSON的庫,而在JavaScript中,我們可以直接使用JSON,因為JavaScript內建了JSON的解析。把JavaScript物件變成JSON,就是把這個物件序列化成一個JSON格式的字串,這樣才能夠通過網路傳遞給其他計算機。如果我們收到一個JSON格式的字串,只需要把它反序列化成一個JavaScript物件,就可以在JavaScript中直接使用這個物件了。
Json的序列化和反序列化
正如一道菜做好後,需要裝在盤子裡端給顧客,前後端的資料傳輸也是如此。資料通過指定格式,將傳輸的物件序列化為二進位制資料流,然後再通過反序列化將資料流內容轉化成為對應的資料物件。
JSON中的資料形式與轉化方式
在JSON中,資料有以下幾種形式:
- 物件:一個沒有順序的"鍵/值",格式如
- 陣列:用以設定數值順序,格式如
- 字串:任意數量的Unicode字元,格式如
進行資料序列化和反序列化的方式有以下三種:
- 使用JavaScriptSerializer類
- 使用DataContractJsonSerializer類
- 使用JSON.NET類庫
以JavaScriptSerializer類為例,
//建立使用者列表
List<UserInfo> userList = new List<UserInfo>();
userList.Add(new UserInfo() { ID = 1, Name = "張三", CreateTime = DateTime.Now });
userList.Add(new UserInfo() { ID = 2, Name = "李四", CreateTime = DateTime.Now });
userList.Add(new UserInfo() { ID = 2, Name = "王五" });
//建立一個JavaScriptSerializer物件
JavaScriptSerializer serializer = new JavaScriptSerializer();
//將使用者列表序列化成JSON
string serializedResult = serializer.Serialize(userList);
//將JOSN反序列化成使用者列表
List<UserInfo> deserializeResult = serializer.Deserialize<List<UserInfo>>(serializedResult);
只需要呼叫對應方法,就可以直接實現對資料內容的序列化。
你以為到這裡就結束了嗎,當然沒有。在實際應用中,資料本身的處理並沒有什麼難度,真正需要考慮解決的問題是,資料本身附加的屬性、設定。就以我們自身為例,客戶在純前端電子表格中對JSON資料傳輸的真實需求是,這段資料需要保證所有視覺化內容的完整傳輸。
純前端表格中的JSON資料處理
在實際處理使用者需求時,使用者在設定好如下圖單元格後,不僅僅是單元格記憶體在數字,還會遇到單元格本身的樣式、自定義函式、 自定義格式、自定義函式迷你圖、自定義標籤,以及自定義行篩選。
我們開啟相關的程式碼,可以清楚地看到在格式中這些對單元格的設定,都被儲存了下來。
在這個圖中,我們可以看到不同型別的資料內容都可以完成序列化和反序列化的過程。在使用自定義序列化的過程中,檢視相關程式碼,處理序列化的核心是typeName 欄位在呼叫toJSON函式的過程,比如,可以將此類姓名和window物件聯絡。而反序列化時,呼叫 getTypeFromString 函式來獲取型別名並且構造型別例項物件,然後呼叫型別例項上的 fromJSON方法。
此外還有許多其他的屬性內容,下面列舉其他樣式設定的例子:
背景圖片:
//這個例子設定了backgroundImageLayout屬性。
var style = new GC.Spread.Sheets.Style();
style.backColor = "lightgreen";
style.backgroundImage = "/css/images/quarter1.png";
style.backgroundImageLayout = GC.Spread.Sheets.ImageLayout.center;
activeSheet.setStyle(1,1,style,GC.Spread.Sheets.SheetArea.viewport);
水印設定:
//此示例設定水印的單元格填充。
var type = new GC.Spread.Sheets.Style();
type.watermark = "User name";
type.cellPadding = "20";
type.labelOptions = {alignment:GC.Spread.Sheets.LabelAlignment.topLeft, visibility: GC.Spread.Sheets.LabelVisibility.visible};
activeSheet.setStyle(0, 1, type);
activeSheet.getRange(0, -1, 1, -1, GC.Spread.Sheets.SheetArea.viewport).height(60);
activeSheet.getRange(-1, 1, -1, 1).width(150);
var combo = new GC.Spread.Sheets.CellTypes.ComboBox();
combo.items([{ text: "Oranges", value: "11k" }, { text: "Apples", value: "15k" }, { text: "Grape", value: "100k" }]);
combo.editorValueType(GC.Spread.Sheets.CellTypes.EditorValueType.text);
activeSheet.setCellType(2, 1, combo, GC.Spread.Sheets.SheetArea.viewport);
activeSheet.getCell(2, 1, GC.Spread.Sheets.SheetArea.viewport).watermark("ComboBox Cell Type").cellPadding('10 10 20 10');
activeSheet.getCell(2, 1, GC.Spread.Sheets.SheetArea.viewport).labelOptions({alignment: GC.Spread.Sheets.LabelAlignment.bottomCenter, foreColor: 'yellowgreen', font: 'bold 15px Arial'});
activeSheet.getRange(2, -1, 1, -1, GC.Spread.Sheets.SheetArea.viewport).height(60);
主題字型:
//這個例子使用了themeFont屬性。
var style = new GC.Spread.Sheets.Style();
style.formatter = "0.000%";
style.themeFont = "Body";
activeSheet.setStyle(1,1,style,GC.Spread.Sheets.SheetArea.viewport);
activeSheet.getCell(1,1).value("11");
還有許多對於單元格的設定,這些樣式內容都可以被完整儲存下來,作為json資料進行傳輸,帶來真正的表格json資料傳輸的便利。
使用過程中需要注意以下問題:
- 給 typeName 欄位設定完整的型別名字串(如果有名稱空間也應包含名稱空間)。
- 如果自定義型別有迴圈依賴或是你希望減小JSON 資料的大小,亦或是你有其他更高階的需求,那麼你的自定義型別需要重寫toJSON和fromJSON方法。
- 如果自定義型別定義在一個閉包中,換句話說,你不希望將自定義型別定義在 window 物件上,你需要重寫 getTypeFromString 函式來手動解析型別的字串。
程式碼示例:
GC.Spread.Sheets.getTypeFromString = function(typeString) {
switch (typeString) {
case "MyFormatter":
return MyFormatter;
case "MyRowFilter":
return MyRowFilter;
default:
return oldFun.apply(this, arguments);
}
};
MyTag.prototype.toJSON = function() {
return {
typeName: this.typeName, //necessary
name: this.name,
age: this.age
};
};
MyTag.prototype.fromJSON = function(settings) {
if (settings.name !== undefined) {
this.name = settings.name;
}
if (settings.age !== undefined) {
this.age = settings.age;
}
};
總結
本文詳細為大家介紹了資料傳輸從XML到JSON的故事,以及json進行序列化和反序列化的工作原理,同時帶大家瞭解了在前端電子表格中要想完全實現整個內容的資料序列化和反序列化應該如何做。
後續也會為大家帶來更多有趣或者嚴肅的內容~
覺得不錯,點個贊再走吧。