最近寫 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 字元串長度逐字解析,每個字元基本上隻處理一次,是以,字元串長度對性能的影響已經被降到了最低。