详解电子表格中的json数据:序列化与反序列化

526 阅读6分钟

从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进行序列化和反序列化的工作原理,同时带大家了解了在前端电子表格中要想完全实现整个内容的数据序列化和反序列化应该如何做。

后续也会为大家带来更多有趣或者严肃的内容~

觉得不错,点个赞再走吧。