首先声明, 由于个人在写这个C#对象JSON序列化之前没有查看过相关JSON序列化的文章和书籍, 所以实现步骤和达到的效果可能与大家所知道的不一样,我的观点是,方便使用即可。 由于技术有限,这个类库会有一些遗漏的元素和不足之处, 如果有其他见解,还请不吝赐教。
在编写web应用程序的时候,我们经常会用到ajax技术,而在客户端与服务器进行ajax异步通信的时候,我们往往需要从客户端提交一些数据到服务器,或是从服务器响应一些数据给客户端,但由于客户端与服务器端的数据格式不统一, 所以我们无法直接将服务器端的C#对象直接发送给客户端,这时候我们就需要用到JSON对象。
ASP.NET的MVC框架中直接提供了JSON对象序列化的方法,但是ASP.NET中貌似没有直接提供,可以通过引用第三方插件或者是MVC中的类库来使用JSON序列化,不过由于个人比较懒,不想引用,加上想巩固一下之前学的一些知识,所以就决定自己写一个够用的C#的JSON序列化和客户端JavaScript的反序列化了(实际上客户端的JavaScript反序列化不是我写的。。我只是改了一点点)。
上面都是扯淡,下面开始介绍这个自定义JSON序列化类型的功能:
C#服务器端:
功能:将C#对象序列化为一个JSON字符串
命名空间:HourglassHelper
类名:HourglassJson(静态类)
主要方法:string Parse(静态方法),该方法有四个重载:
·Parse(params object[] datas)
·Parse(HgJSonItemArrayOption arrayOption, params object[] datas)
·Parse(bool convertOblique, params object[] datas)
·Parse(HgJSonItemArrayOption arrayOption, bool convertOblique, params object[] datas)
参数说明:datas:对象数组,调用时可传入任意多个对象
convertOblique:是否将字符串类型对象的值中的"/"进行转义,以防止其进行转义,默认值true(也就是默认会将"/"转换为"//")
arrayOption:数组对象设置,默认值HgJSonItemArrayOption.None
·HgJSonItemArrayOption.AllObject //将所有对象都封装为JavaScript数组
·HgJSonItemArrayOption.OnlyComplex //仅仅封装复杂对象为JavaScript数组(不论长度)
·HgJSonItemArrayOption.OnlyOriginal //仅仅封装初始传入对象为JavaScript数组(不论长度,但是长度大于1的C#数组对象还是会被封装为JavaScript数组)
·HgJSonItemArrayOption.OnlyComplexWithoutOriginal //仅仅封装复杂对象为JavaScript数组,如果初始传入对象的长度为1,那么就不封装初始传入对象
·HgJSonItemArrayOption.None //长度大于1的复杂对象封装为JavaScript数组
JavaScript客户端:
额、好吧,其实客户端解析我服务器端序列化后JSON对象代码是从《JavaScript语言精粹》最后面的一个Json_Parse上直接copy过来的,然后在时候的时候有一点点小问题。那就是服务器端在序列化C#对象的时候,会把Boolean对象的true和false序列化为"True"和"False",这样是开头大写的,但是《JavaScript语言精粹》提供的反序列化代码中,在解析true和false的时候,只将"true"和"false"映射为对应的boolean值,但是不会忽略大小写,这个问题其实是可以在写服务器端代码的时候解决的,不过我认为客户端在解析JSON字符串的时候,在对boolean值进行解析的时候,应该忽略大小写的,所以在解析boolean那块儿稍微改了一下。当然如果觉得这样不合适,可以自行再改回去。
服务器端代码:
/** * Version : 1.0.1 * Author : Hourglass * Date : 2014-1-22 */using System;using System.Collections;using System.Collections.Generic;using System.Linq;using System.Reflection;using System.Text;using System.Web;namespace HourglassHelper{ public static class HourglassJson { private static bool _convertOblique = true; private static HgJSonItemArrayOption _arrayOption = HgJSonItemArrayOption.None; private static bool isCurrentList = false; private static bool isPreviousList = false; ////// 将多个任意类型的对象转换为JSon对象 /// /// 需要被转换的JSon对象 ///JSon字符串值 public static string Parse(params object[] datas) { string hgJson; if (datas.Length > 1 || _arrayOption == HgJSonItemArrayOption.OnlyOriginal) { hgJson = ArrayToHgJson(datas); } else { if (datas.Length == 1 && typeof(IList).IsAssignableFrom(datas[0].GetType())) { hgJson = ArrayToHgJson(datas); hgJson = "[" + hgJson.Trim('[', ']') + "]"; } else { hgJson = ConvertAsHgJson(datas[0]); } if (_arrayOption == HgJSonItemArrayOption.OnlyComplexWithoutOriginal) { hgJson = hgJson.Remove(0, 1).Remove(hgJson.Length - 2, 1); } } return hgJson; } ////// 将多个任意类型的对象转换为JSon对象 /// /// 是否不论传入数据个数是否大于1,始终将JSON保存为数组,默认false /// 需要被转换的JSon对象 ///JSon字符串值 public static string Parse(HgJSonItemArrayOption arrayOption, params object[] datas) { _arrayOption = arrayOption; return Parse(datas); } ////// 将多个任意类型的对象转换为JSon对象 /// /// 是否需要将字符串中的'/'替换为'//',默认true /// 需要被转换的JSon对象 ///JSon字符串值 public static string Parse(bool convertOblique, params object[] datas) { if (datas.Length == 0) { return ConvertAsHgJson(convertOblique); } _convertOblique = convertOblique; return Parse(datas); } ////// 将多个任意类型的对象转换为JSon对象 /// /// 是否不论传入数据个数是否大于1,始终将JSON保存为数组,默认false /// 是否需要将字符串中的'/'替换为'//',默认true /// 需要被转换的JSon对象 ///JSon字符串值 public static string Parse(HgJSonItemArrayOption arrayOption, bool convertOblique, params object[] datas) { _arrayOption = arrayOption; _convertOblique = convertOblique; return Parse(datas); } ////// 将一个对象转换为自定义JSon对象 /// /// 需要被转换的对象 ///JSon字符串值 private static string ConvertAsHgJson(object item) { string jsonValue; if (item == null) { return "null"; } Type type = item.GetType(); string typeName = type.Name.ToLower(); switch (typeName) { case "string": case "char": case "datetime": if (_convertOblique) { jsonValue = "\"" + EasyTypeToHgJson(item).Replace("\\", "\\\\") + "\""; } else { jsonValue = "\"" + EasyTypeToHgJson(item) + "\""; } break; case "byte": case "int16": case "int32": case "int64": case "uint16": case "uint32": case "uint64": case "boolean": case "single": case "double": case "decimal": jsonValue = EasyTypeToHgJson(item); break; default: { if (typeof(IList).IsAssignableFrom(type)) {//实现IList(包括数组) isCurrentList = true; List
客户端代码:
(function (window) { var json_parse = (function () { var at, // The index of the current character ch, // The current character escapee = { '"': '"', '\\': '\\', '/': '/', b: '\b', f: '\f', n: '\n', r: '\r', t: '\t' }, text, error = function (m) {// Call error when something is wrong. throw { name: 'SyntaxError', message: m, at: at, text: text }; }, next = function (c, cc) {// If a c parameter is provided, verify that it matches the current character.cc -> check case if (cc) { if (c && c.toLowerCase() !== ch.toLowerCase()) { error("Expected '" + c + "' instead of '" + ch + "'"); } } else { if (c && c !== ch) { error("Expected '" + c + "' instead of '" + ch + "'"); } } // Get the next character. When there are no more characters, // return the empty string. ch = text.charAt(at); at += 1; return ch; }, number = function () {// Parse a number value. var number, string = ''; if (ch === '-') { string = '-'; next('-'); } while (ch >= '0' && ch <= '9') { string += ch; next(); } if (ch === '.') { string += '.'; while (next() && ch >= '0' && ch <= '9') { string += ch; } } if (ch === 'e' || ch === 'E') { string += ch; next(); if (ch === '-' || ch === '+') { string += ch; next(); } while (ch >= '0' && ch <= '9') { string += ch; next(); } } number = +string; if (!isFinite(number)) { error("Bad number"); } else { return number; } }, string = function () {// Parse a string value. var hex, i, string = '', uffff; // When parsing for string values, we must look for " and \ characters. if (ch === '"') { while (next()) { if (ch === '"') { next(); return string; } else if (ch === '\\') { next(); if (ch === 'u') { uffff = 0; for (i = 0; i < 4; i += 1) { hex = parseInt(next(), 16); if (!isFinite(hex)) { break; } uffff = uffff * 16 + hex; } string += String.fromCharCode(uffff); } else if (typeof escapee[ch] === 'string') { string += escapee[ch]; } else { break; } } else { string += ch; } } } error("Bad string"); }, white = function () {// Skip whitespace. while (ch && ch <= ' ') { next(); } }, word = function () {// true, false, or null. switch (ch.toLowerCase()) { case 't': next('t', true); next('r', true); next('u', true); next('e', true); return true; case 'f': next('f', true); next('a', true); next('l', true); next('s', true); next('e', true); return false; case 'n': next('n', true); next('u', true); next('l', true); next('l', true); return null; } error("Unexpected '" + ch + "'"); }, value, // Place holder for the value function. array = function () {// Parse an array value. var array = []; if (ch === '[') { next('['); white(); if (ch === ']') { next(']'); return array; // empty array } while (ch) { array.push(value()); white(); if (ch === ']') { next(']'); return array; } next(','); white(); } } error("Bad array"); }, object = function () {// Parse an object value. var key, object = {}; if (ch === '{') { next('{'); white(); if (ch === '}') { next('}'); return object; // empty object } while (ch) { key = string(); white(); next(':'); if (Object.hasOwnProperty.call(object, key)) { error('Duplicate key "' + key + '"'); } object[key] = value(); white(); if (ch === '}') { next('}'); return object; } next(','); white(); } } error("Bad object"); }; value = function () { // Parse a JSON value. It could be an object, an array, a string, a number, // or a word. white(); switch (ch) { case '{': return object(); case '[': return array(); case '"': return string(); case '-': return number(); default: return ch >= '0' && ch <= '9' ? number() : word(); } }; // Return the json_parse function. It will have access to all of the above // functions and variables. return function (source, reviver) { var result; text = source; at = 0; ch = ' '; result = value(); white(); if (ch) { error("Syntax error"); } return typeof reviver === 'function' ? function walk(holder, key) { var k, v, value = holder[key]; if (value && typeof value === 'object') { for (k in value) { if (Object.hasOwnProperty.call(value, k)) { v = walk(value, k); if (v !== undefined) { value[k] = v; } else { delete value[k]; } } } } return reviver.call(holder, key, value); }({ '': result }, '') : result; }; }()); window.hgJSonParse = json_parse;})(window);
文件下载区:
①
① P21第一段代码中的if语句有错,应该是if(typeof Object.create !== 'function')。英文版中统一使用的是Object.create,非修订版中统一使用的是Object.target,而修订版中只有这个地方判断的时候用的是Object.target,其他地方都是Object.create