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那块儿稍微改了一下。当然如果觉得这样不合适,可以自行再改回去。

 

服务器端代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
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);
                }
                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)
        {
            _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的复杂对象封装为数组
    }
}

 

 

客户端代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
(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);

 

文件下载区:

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

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

服务端代码下载

客户端代码下载

 

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

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

上一篇:创建Django项目(一)


下一篇:[转] 外行值得一读的5本经典数学书