用 js 实现 JSON.parse 以及 JSON.stringify

tech2025-03-07  17

最近写 asp 发现一个问题,古老的 asp 缺少对 json 数据的安全支持,对象 转字符的方法没有,字符转对象 虽然可以通过 eval("(" + json + ")") 的方式实现 json 解析,但是容易被不良人当作木马入口,你期待的可能是一个正常的 json 字符串,但人家传过来的有可能是 (function() { Response.Write("Hacked"); })() 之类的 js 执行语句。所以,用 js 实现了 parse 和 stringify 方法:

var JSON = { stringify: tojson, parse: fromjson }; WSH.Echo(JSON.stringify(JSON.parse('[ true, { "key": "value", "arr": [ { } ], "num" : 95.27 }, null, false ]'))); function fromjson(str) { var regTag = /[\{\[\"ntf\d\.]/, i = 0, len = str.length; function newParse() { var s = waitStr(regTag); if(!s) return; switch(s) { case "{": return findObj(); case "[": return findArr(); case "t": return findTrue(); case "f": return findFalse(); case "n": return findNull(); case '"': return findStr(); } return findNum(s); } function findObj() { var obj = new Object; while(i < len) { var s = waitStr(/\S/); if(s == "}") break; if(s == ",") continue; var key = findStr(); waitStr(/\:/); obj[key] = newParse(); } return obj; } function findArr() { var arr = new Array; while(i < len) { var s = waitStr(/\S/); if(s == "]") break; if(s == ",") continue; i--; arr.push(newParse()); } return arr; } function findTrue() { i += 3; return true; } function findFalse() { i += 4; return false; } function findNull() { i += 3; return null; } function findStr() { var s = new Array; while(i < len) { var _s = str.charAt(i++); if(_s == '"') break; if(_s == "\\") _s = strDec(str.charAt(i++)); s.push(_s); } return s.join(""); } function findNum(s) { while(i < len) { var _s = str.charAt(i++); if(!/[\d\.]/.test(_s)) break; s += _s; } i--; return s - 0; } function waitStr(reg) { while(i < len) { var s = str.charAt(i++); if(reg.test(s)) return s; } return ""; } var dic = { t: "\t", n: "\n", r: "\r", b: "\b", f: "\f", v: "\x0b" }; function strDec(c) { switch(c) { case "x": return unescape("%" + str.slice(i, i += 2)); case "u": return unescape("%u" + str.slice(i, i += 4)); } return c in dic ? dic[c] : c; }; return newParse(); } function tojson(obj) { switch(typeof obj) { case "string": return toStr(); case "number": return toNum(); case "object": return toObj(obj); case "boolean": return toBool(); case "date": return toTime(); case "function": return obj; case "undefined": return '"undefined"'; default: return "unknown"; } function toStr() { return '"' + obj.replace(/[\"\\]/g, function(str) { return "\\" + str; }).replace(/\r/g, "\\r").replace(/\n/g, "\\n") + '"'; } function toNum() { return obj; } function toBool() { return obj ? "true" : "false"; } function toObj() { if(!obj) return "null"; if(obj instanceof Array) return toArr(); var arr = new Array; for(var x in obj) arr.push(tojson(x + "") + ":" + tojson(obj[x])); return "{" + arr.join(",") + "}"; } function toArr() { var arr = new Array; for(var i = 0; i < obj.length; i++) arr.push(tojson(obj[i])); return "[" + arr.join(",") + "]"; } function toTime() { var t = new Date(obj - 0); var str = t.getFullYear() + "-"; str += z(t.getMonth() + 1) + "-"; str += z(t.getDate()) + " "; str += z(t.getHours()) + ":"; str += z(t.getMinutes()) + ":"; str += z(t.getSeconds()); return '"' + str + '"'; } function z(num) { return num < 10 ? "0" + num : num; } }

由于从字符转对象的解析有些复杂,所以没有做格式校验,只实现了安全解析。因此,错误的格式不会导致解析异常,也不会影响正常解析,正常的日常使用是没问题的。性能方面,因为是根据 json 字符串长度逐字解析,每个字符基本上只处理一次,因此,字符串长度对性能的影响已经被降到了最低。

最新回复(0)