C#对象JSON序列化与客户端JavaScript反序列化

  首先声明, 由于个人在写这个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;

        /// <summary>
        /// 将多个任意类型的对象转换为JSon对象
        /// </summary>
        /// <param name="datas">需要被转换的JSon对象</param>
        /// <returns>JSon字符串值</returns>
        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;
        }

        /// <summary>
        /// 将多个任意类型的对象转换为JSon对象
        /// </summary>
        /// <param name="alwaysArray">是否不论传入数据个数是否大于1,始终将JSON保存为数组,默认false</param>
        /// <param name="datas">需要被转换的JSon对象</param>
        /// <returns>JSon字符串值</returns>
        public static string Parse(HgJSonItemArrayOption arrayOption, params object[] datas)
        {
            _arrayOption = arrayOption;
            return Parse(datas);
        }

        /// <summary>
        /// 将多个任意类型的对象转换为JSon对象
        /// </summary>
        /// <param name="convertOblique">是否需要将字符串中的'/'替换为'//',默认true</param>
        /// <param name="datas">需要被转换的JSon对象</param>
        /// <returns>JSon字符串值</returns>
        public static string Parse(bool convertOblique, params object[] datas)
        {
            if (datas.Length == 0)
            {
                return ConvertAsHgJson(convertOblique);
            }
            _convertOblique = convertOblique;
            return Parse(datas);
        }

        /// <summary>
        /// 将多个任意类型的对象转换为JSon对象
        /// </summary>
        /// <param name="alwaysArray">是否不论传入数据个数是否大于1,始终将JSON保存为数组,默认false</param>
        /// <param name="convertOblique">是否需要将字符串中的'/'替换为'//',默认true</param>
        /// <param name="datas">需要被转换的JSon对象</param>
        /// <returns>JSon字符串值</returns>
        public static string Parse(HgJSonItemArrayOption arrayOption, bool convertOblique, params object[] datas)
        {
            _arrayOption = arrayOption;
            _convertOblique = convertOblique;
            return Parse(datas);
        }

        /// <summary>
        /// 将一个对象转换为自定义JSon对象
        /// </summary>
        /// <param name="item">需要被转换的对象</param>
        /// <returns>JSon字符串值</returns>
        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<object> objs = new List<object>();
                            foreach (object it in (IEnumerable)item)
                            {
                                objs.Add(it);
                            }
                            jsonValue = HourglassJson.ArrayToHgJson(objs.ToArray<object>());
                            isCurrentList = false;
                            isPreviousList = true;
                        }
                        else if (typeof(IDictionary).IsAssignableFrom(type))
                        {//实现IDictionary
                            Dictionary<string, object> dic = new Dictionary<string, object>();
                            foreach (var it in (IEnumerable)item)
                            {
                                Type itType = it.GetType();
                                string key = itType.InvokeMember("Key", BindingFlags.GetProperty, null, it, null).ToString();
                                object value = itType.InvokeMember("Value", BindingFlags.GetProperty, null, it, null);
                                dic.Add(key, value);
                            }
                            jsonValue = DicToHgJson(dic);
                        }
                        else
                        {//用户自定义类或结构体(class, struct)
                            jsonValue = ClassStructToHgJson(item);
                        }
                        //待补充,非IsPrimitive的类型应该还有别的类型
                        if (!isCurrentList && !isPreviousList && _arrayOption != HgJSonItemArrayOption.None)
                        {
                            if (_arrayOption == HgJSonItemArrayOption.OnlyComplex || _arrayOption == HgJSonItemArrayOption.OnlyComplexWithoutOriginal)
                            {
                                jsonValue = "[" + jsonValue + "]";
                            }
                        }
                    }
                    break;
            }
            if (!isPreviousList && _arrayOption == HgJSonItemArrayOption.AllObject)
            {
                jsonValue = "[" + jsonValue + "]";
            }
            isPreviousList = false;
            return jsonValue;
        }

        /// <summary>
        /// 将Class和Struct转换为自定义JSon对象
        /// </summary>
        /// <param name="item">Class或Struct对象</param>
        /// <returns>JSon字符串值</returns>
        private static string ClassStructToHgJson(object item)
        {
            StringBuilder classStructHgJson = new StringBuilder();
            Type type = item.GetType();
            PropertyInfo[] properties = type.GetProperties();
            classStructHgJson.Append("{");
            foreach (PropertyInfo p in properties)
            {
                object propertyValue = type.InvokeMember(p.Name, BindingFlags.GetProperty, null, item, null);
                classStructHgJson.Append(string.Format(""{0}":{1}", p.Name, HourglassJson.ConvertAsHgJson(propertyValue)));
                classStructHgJson.Append(",");
            }
            classStructHgJson.Remove(classStructHgJson.Length - 1, 1);
            classStructHgJson.Append("}");
            return classStructHgJson.ToString();
        }

        /// <summary>
        /// 将数组转换为自定义JSon对象
        /// </summary>
        /// <param name="items">数组对象</param>
        /// <returns>JSon字符串值</returns>
        private static string ArrayToHgJson(object[] items)
        {
            StringBuilder hgJson = new StringBuilder();
            hgJson.Append("[");
            foreach (object item in items)
            {
                string itemHgJson = ConvertAsHgJson(item);
                hgJson.Append(itemHgJson);
                hgJson.Append(",");
            }
            hgJson.Remove(hgJson.Length - 1, 1);
            hgJson.Append("]");
            return hgJson.ToString();
        }

        /// <summary>
        /// 将IDictionary转换为自定义JSon对象
        /// </summary>
        /// <param name="dictionary">IDictionary对象</param>
        /// <returns>JSon字符串值</returns>
        private static string DicToHgJson(IDictionary<string, object> dictionary)
        {
            StringBuilder dicHgJsonBuilder = new StringBuilder();
            dicHgJsonBuilder.Append("{");
            foreach (KeyValuePair<string, object> item in dictionary)
            {
                string itemValueHgJson = HourglassJson.ConvertAsHgJson(item.Value);
                string itemHgJson = string.Format(""{0}":{1}", item.Key, itemValueHgJson);
                dicHgJsonBuilder.Append(itemHgJson);
                dicHgJsonBuilder.Append(",");
            }
            dicHgJsonBuilder.Remove(dicHgJsonBuilder.Length - 1, 1);
            dicHgJsonBuilder.Append("}");
            return dicHgJsonBuilder.ToString();
        }

        /// <summary>
        /// 简单类型转自定义JSon对象。
        /// 简单类型包括string, char, byte, short, int, long, ushort, uint, ulong, float, double, decimal, bool, datetime
        /// </summary>
        /// <param name="item">简单类型对象</param>
        /// <returns>JSon字符串值</returns>
        private static string EasyTypeToHgJson(object item)
        {
            return item.ToString();
        }
    }
    public enum HgJSonItemArrayOption
    {
        AllObject,          //将所有对象都封装为数组
        OnlyComplex,        //仅仅封装复杂对象为数组(不论长度)
        OnlyOriginal,       //仅仅封装初始传入对象为数组(不论长度,但是长度大于1的数组对象还是会被封装为数组)
        OnlyComplexWithoutOriginal,     //仅仅封装复杂对象,如果初始传入对象的长度为1,那么就不封装初始传入对象
        None                //长度大于1的复杂对象封装为数组
    }
}

  

客户端代码:

(function (window) {
    var json_parse = (function () {
        var at,     // The index of the current character
            ch,     // The current character
            escapee = {
                '"': '"',
                '\': '\',
                '/': '/',
                b: '',
                f: 'f',
                n: '
',
                r: '
',
                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);

文件下载区:

《JavaScript语言精粹》-中文版_修订版

《JavaScript语言精粹》-英文原版

服务端代码下载

客户端代码下载

① P21第一段代码中的if语句有错,应该是if(typeof Object.create !== 'function')。英文版中统一使用的是Object.create,非修订版中统一使用的是Object.target,而修订版中只有这个地方判断的时候用的是Object.target,其他地方都是Object.create

原文地址:https://www.cnblogs.com/hourglasser/p/3525695.html