• 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

  • 相关阅读:
    202012-2 期末预测之最佳阈值
    [蓝桥杯][2017年第八届真题]k倍区间
    116. 飞行员兄弟
    P1985 [USACO07OPEN]翻转棋 Fliptile S
    P2882 [USACO07MAR]Face The Right Way G
    730. 机器人跳跃问题
    202012-1 期末预测之安全指数
    SpringMVC 的 JDBC 做 增删改查后 一些总结
    22. VUE 的 V-model 修饰符
    21. VUE 的 V-model 指令(双向绑定input)【主要绑定表单】
  • 原文地址:https://www.cnblogs.com/hourglasser/p/3525695.html
Copyright © 2020-2023  润新知