给定一个JSON数据字符串,如何安全地将该字符串转换为JavaScript对象?

很明显,我可以用这样的方式来做这件事:

var obj = eval("(" + json + ')');

但这让我很容易受到包含其他代码的JSON字符串的攻击,简单地eval似乎非常危险。


当前回答

官方文件:

方法解析JSON字符串,构造字符串所描述的JavaScript值或对象。可以提供可选的reviver函数,以便在返回结果对象之前对其执行转换。

语法:

JSON.parse(text[, reviver])

参数:

文本:要解析为JSON的字符串。有关JSON语法的描述,请参见JSON对象。

reviver(可选):如果是函数,这规定了在返回之前如何转换最初通过解析生成的值。

返回值

与给定JSON文本对应的Object。

例外情况

如果要分析的字符串不是有效的JSON,则引发SyntaxError异常。

其他回答

我不确定是否有其他方法可以做到这一点,但以下是在Prototype(JSON教程)中如何做到的。

new Ajax.Request('/some_url', {
  method:'get',
  requestHeaders: {Accept: 'application/json'},
  onSuccess: function(transport){
    var json = transport.responseText.evalJSON(true);
  }
});

使用true作为参数调用evalJSON()将净化传入字符串。

另一种选择

const json=“{”水果“:”菠萝“,”手指“:10}”让j0s,j1s,j2s,j3sconsole.log(`{“${j0s=“fruit”}”:“${j1s=“菠萝”}”,“${j2s=“fingers”}”:${j3s=“10”}}`)

我知道,老问题是,但是没有人注意到这个解决方案,因为它使用了new Function(),一个返回数据的匿名函数。


举个例子:

 var oData = 'test1:"This is my object",test2:"This is my object"';

 if( typeof oData !== 'object' )
  try {
   oData = (new Function('return {'+oData+'};'))();
  }
  catch(e) { oData=false; }

 if( typeof oData !== 'object' )
  { alert( 'Error in code' ); }
 else {
        alert( oData.test1 );
        alert( oData.test2 );
      }

这稍微安全一点,因为它在函数内部执行,而不直接在代码中编译。因此,如果其中有函数声明,它将不会绑定到默认的窗口对象。

我使用它来简单快速地“编译”DOM元素(例如数据属性)的配置设置。

使用“JSON.parse()”中的简单代码示例:

var jsontext = '{"firstname":"Jesper","surname":"Aaberg","phone":["555-0100","555-0120"]}';
var contact = JSON.parse(jsontext);

并将其反转:

var str = JSON.stringify(arr);

表演

这个问题已经有了很好的答案,但我对性能很好奇,今天2020.09.21我在Chrome v85、Safari v13.1.2和Firefox v80上对MacOs HighSierra 10.13.6进行了测试,以确定所选的解决方案。

后果

eval/Function(A,B,C)方法在Chrome上很快(但对于大深度对象N=1000,它们会崩溃:“最大堆栈调用超过”)eval(A)在所有浏览器上都是快速/中等速度JSON.parse(D,E)在Safari和Firefox上速度最快

细节

我执行4个测试用例:

对于小的浅对象对于这里的小深对象对于这里的大型浅层物体对于这里的大深度对象

上述测试中使用的对象来自HERE

let obj_ShallowSmall={字段0:假,字段1:真,字段2:1,字段3:0,字段4:空,字段5:[],字段6:{},field7:“text7”,字段8:“text8”,}让obj_DepSmall={级别0:{级别1:{级别2:{级别3:{级别4:{级别5:{级别6:{第7级:{级别8:{第9级:[[[[[[[[]],}}}}}}}}},};让obj_ShallowBig=数组(1000).fill(0).reduce((a,c,i)=>(a['field'+i]=getField(i),a),{});让obj_DepBig=genDeepObject(1000);// ------------------//显示对象// ------------------console.log('obj_ShallowSmall:',JSON.stringify(obj_SharlowSmall));console.log('obj_DepSmall:',JSON.stringify(obj_DeepSmall));console.log('obj_ShallowBig:',JSON.stringify(obj_SharlowBig));console.log('obj_DepBig:',JSON.stringify(obj_DeepBig));// ------------------//助手// ------------------函数getField(k){设i=k%10;如果(i==0)返回false;如果(i==1)返回真;如果(i==2)返回k;如果(i==3)返回0;如果(i==4)返回null;如果(i==5)返回[];如果(i==6)返回{};如果(i>=7)返回“text”+k;}函数genDeepObject(N){//生成:{level0:{level1:{…levelN:{end:[[[…N次…['abc']…]]}}}}…}}让obj={};设o=obj;设arr=[];设a=arr;for(设i=0;i<N;i++){o['level'+i]={};o=o['level'+i];设aa=[];a.推(aa);a=aa;}a[0]=“公元前”;o['end']=arr;返回obj;}

下面的片段显示了所选的解决方案

//第三节:https://stackoverflow.com/q/45015/860099函数A(json){return eval(“(”+json+')');}// https://stackoverflow.com/a/26377600/860099函数B(json){return(新函数('return('+json+')'))()}//改进的https://stackoverflow.com/a/26377600/860099函数C(json){return函数('return('+json+')')()}//第三节:https://stackoverflow.com/a/5686237/860099函数D(json){返回JSON.parse(JSON);}//第三节:https://stackoverflow.com/a/233630/860099函数E(json){return$.parseJSON(json)}// --------------------//测试// --------------------让json=“{“a”:“abc”,“b”:“123”,“d”:[1,2,3],“e”:{”a“:1,”b“:2,”c“:3}}”;[A,B,C,D,E]映射(f=>{控制台日志(f.name+“”+JSON.stringfy(f(JSON)))})<script src=“https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js“></script>此shippet只显示性能测试中使用的函数,而不执行测试本身!

下面是铬的示例结果