详解电子表格中的 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 进行序列化和反序列化的工作原理,同时带大家了解了在前端电子表格中要想完全实现整个内容的数据序列化和反序列化应该如何做。
后续也会为大家带来更多有趣或者严肃的内容~
觉得不错,点个赞再走吧。