最近写 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 字符串长度逐字解析,每个字符基本上只处理一次,因此,字符串长度对性能的影响已经被降到了最低。