詳解電子表格中的json數據:序列化與反序列化

從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進行序列化和反序列化的工作原理,同時帶大家瞭解了在前端電子表格中要想完全實現整個內容的數據序列化和反序列化應該如何做。

後續也會爲大家帶來更多有趣或者嚴肅的內容~

覺得不錯,點個贊再走吧。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章