從字符串中解析出JSON


JSON介紹http://json.org/

這個解析JSON,也是Prototype源碼淺析的一個鋪墊。

下面是一個開篇例子,其中response是從服務器獲得的JSON字符串:

    var response_1 = "{\"user\":\"xesam\",\"info\":{\"age\":\"24\"}}";
var response_2 = "{'user' : 'xesam'}";
var response_3 = "console.log('xss')";

var obj_1_1 = eval('(' + response_1 + ')');
console.log('obj_1_1',obj_1_1);
var obj_1_2 = eval('(' + response_2 + ')');
console.log('obj_1_2',obj_1_2);
var obj_1_3 = eval('(' + response_3 + ')');
console.log('obj_1_3',obj_1_3);

var obj_2_1 = (new Function('return ' + response_1))();
console.log('obj_2_1',obj_2_1);
var obj_2_2 = (new Function('return ' + response_2))();
console.log('obj_2_2',obj_2_2);
var obj_2_3 = (new Function('return ' + response_3))();
console.log('obj_2_3',obj_2_3);

var obj_3_1 = JSON.parse(response_1);
console.log('obj_3_1',obj_3_1);
try{
var obj_3_2 = JSON.parse(response_2);
console.log('obj_3_2',obj_3_2);
}catch(e){
console.log('obj_3_2','error');
}
try{
var obj_3_3 = JSON.parse(response_3);
console.log('obj_3_3',obj_3_3);
}catch(e){
console.log('obj_3_3','error');
}

直接來結果:

obj_1_1 Object 
obj_1_2 Object
xss
obj_1_3 undefined
obj_2_1 Object
obj_2_2 Object
xss
obj_2_3 undefined
obj_3_1 Object
obj_3_2 error
obj_3_3 error


  可見,對於瀏覽器自帶的標准JSON解析器來說,response_2和response_3是非法的。因此,我們可以更進一步的了解到JSON的幾點主要的地方。

  第一、對於key/value的形式,每一個key都需要有引號(這個和javascript原生對象可有可無的引號不同),需要是字符串(這種字符串形式上更類似於C語言里面的字符串,必須是雙引號,這個和javascript原生對象引號可以使雙引號也可以是單引號不同)。

上面的圖示是對JSON中string的要求

  第二、JSON本來就是一種交換數據的格式,因此,里面是不應該包含需要執行的javascript代碼的。

所以,只有嚴格符合標准的JSON字符串形式,才能通過JSON.parse的解析。

對於大部分現代瀏覽器而言,window.JSON是一個內置的對象,但是對於IE6/7來說,這個對象是缺失的,所以,需要借助例子中的前兩種形式eval和new Function。

  平常而言,eval用的比較多,但是eval夠強大,所以夠危險。

  response_2被解析成對象是eval本身的功能,跟JSON無關。如果服務器返回的是類似response_2的形式,八成是一開始的傳值就有問題,我就犯過這種錯誤。不過如果是服務端生成的,一般倒不會有什么問題。

  response_3被解析成可執行代碼頁是eval本身的功能,是危險的來源,不過好像平時也沒怎么注意。

不過,對於IE6/7來說,要從字符串中解析出來JSON對象,又必須借助於eval(依稀聽說新的ECMAScript標准拋棄了eval?不過這跟IE6/7半毛錢的關系都沒有)。

因此,這一部分可以參考JSON.js的String.prototype.parseJSON

對於

var response_1 = "{\"user\":\"xesam\",\"info\":{\"age\":\"24\"}}";

  這段,String.prototype.parseJSON的轉換流程如下:

  第一,先檢測待轉換字符串的合法性

  第二,eval字符串

  所以response_1的解析情況大致如下:

    (function(){
var responseTemp = response_1.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
var correct = /^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/.test(responseTemp);
if(correct){
var j = eval('(' + response_1+ ')');
return j;
}
throw new SyntaxError('parseJSON');
})()
  replace(/\\./g, '@')先把一些轉義字符替換掉,那個“@”並沒有什么特殊意思,至於為什么@,估計是因為正則里面沒有這個元字符,所以避免沖突,想換做其他的,應該也可以。
  replace(/"[^"\\\n\r]*"/g, '')這一步替換掉了所有引號包含的內容,於是,字符串中剩下的部分就是除去string外可以存在的字符。
  看這個,/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/其實好理解,JSON的value值可以是string,object,array,number,false,true,null等7種,需要引號包含string的內容都處理掉了,所以除了這些,
JSON格式還有
{}表示對象結構,對應,:{}
[]表示數組結構,對應\[\]
0-9 . E e表示數字類型,對應0-9.\-+Ee
true 和 false 對應aflsrtu
null 對應n
其他一些特殊字符對應 \n\r\t
這些組合起來就是上面的一段正則。
下面是完整的JSON.js的源碼,無非是添加了一個遞歸而已,從而可以安全的解析整個嵌套的部分。
(function(s){
s.parseJSON = function (filter) {
var j;
function walk(k, v) {
var i;
if (v && typeof v === 'object') {
for (i in v) {
if (Object.prototype.hasOwnProperty.apply(v, [i])) {
v[i] = walk(i, v[i]);
}
}
}
return filter(k, v);
}
if (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/.test(this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, ''))) {
j = eval('(' + this + ')');
return typeof filter === 'function' ? walk('', j) : j;
}
throw new SyntaxError('parseJSON');
};
})(String.prototype);
 
        
轉載請注明來自小西山子【http://www.cnblogs.com/xesam/
本文地址:http://www.cnblogs.com/xesam/archive/2012/01/09/2317631.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM