【问题标题】:Safely turning a JSON string into an object安全地将 JSON 字符串转换为对象
【发布时间】:2010-09-07 21:13:04
【问题描述】:

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

显然,我可以通过以下方式不安全地执行此操作:

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

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

【问题讨论】:

  • 在大多数语言中,eval 带有额外的风险。 Eval 为黑客的利用敞开了大门。但是,请记住所有 javascript 都在客户端上运行。 预计它将被黑客更改。他们可以 EVAL 任何他们想要的东西,只需使用控制台。您必须在服务器端建立保护。
  • 好的,现在是 2014 年,您永远不应该使用 eval 来解析 JSON 字符串,因为您会将代码暴露给“代码注入”。请改用JSON.parse(yourString)
  • JSON 数据是文字吗?
  • @shanechiu:如果您的意思是标量数据类型,是的。只是一个带有键值语法的字符串。
  • 查看parse()方法的文档:w3schools.com/js/js_json_parse.asp

标签: javascript json parsing


【解决方案1】:

JSON.parse(jsonString) 是一种纯 JavaScript 方法,只要您能保证一个相当现代的浏览器。

【讨论】:

  • 我很确定它对 Node.js 是安全的
  • @vsync 您确实意识到这是 ONLY 纯 Javascript 答案...如果您阅读 javascript 标记的描述,您会看到这个...“除非还包含框架/库的标签,否则需要纯 JavaScript 答案。".. 我给它一个 +1 因为它是唯一的 javascript 答案...
  • 如果您正在使用 NodeJS,我无法加载 jQuery 来将 jsonString 解析为 JSON 对象。所以赞成乔纳森的回答
  • 根据this link,IE8+支持,虽然上面写着:Requires document to be in IE8+ standards mode to work in IE8.
【解决方案2】:

现在不推荐使用 jQuery 方法。请改用此方法:

let jsonObject = JSON.parse(jsonString);

使用已弃用的 jQuery 功能的原始答案

如果您使用的是 jQuery,请使用:

jQuery.parseJSON( jsonString );

这正是您正在寻找的(参见 jQuery documentation)。

【讨论】:

  • 有理由在 JSON.parse() 上使用它吗?
  • jQuery.parseJSON 默认使用JSON.parse(如果存在),因此使用它而不是真实的唯一原因是如果您需要 james.padolsey.com/jquery/#v=1.6.0&fn=jQuery.parseJSON
  • 2016 年更新:从 jQuery 3.0 开始,$.parseJSON 已弃用,您应该改用原生 JSON.parse 方法。
【解决方案3】:

此答案适用于 IE

此答案已过时,Jonathan's answer above (JSON.parse(jsonString)) 现在是最佳答案

JSON.org 具有多种语言的 JSON 解析器,包括四种不同的 JavaScript 解析器。我相信大多数人会考虑将json2.js 作为他们的 goto 实现。

【讨论】:

  • 我希望人们停止对这个答案投反对票。它在 2008 年发布时是准确的。只需支持新的。
  • 如果答案现在已经过时,请考虑更新它。
  • 对于 IE
【解决方案4】:

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

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

并反转它:

var str = JSON.stringify(arr);

【讨论】:

    【解决方案5】:

    这似乎是问题:

    通过 Ajax websocket 等接收的输入,它将是字符串格式,但您需要知道它是否为JSON.parsable。问题是,如果你总是通过JSON.parse 运行它,程序可能会继续“成功”,但你仍然会在控制台中看到一个错误,并带有可怕的"Error: unexpected token 'x'"

    var data;
    
    try {
      data = JSON.parse(jqxhr.responseText);
    } catch (_error) {}
    
    data || (data = {
      message: 'Server error, please retry'
    });
    

    【讨论】:

    • 没有。问题是你期待一个 JSON 对象,最终可能会得到 (function(){ postCookiesToHostileServer(); }()); 甚至在 Node 上下文中更糟糕的东西。
    • 那么 JSON.parse 会清除函数的输入(在这种情况下,作为 IIF --> 对象将无济于事)。似乎解决这个问题的最好方法是尝试/捕捉。 (见编辑)
    【解决方案6】:

    我不确定其他方法,但您可以在 Prototype (JSON tutorial) 中这样做。

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

    使用 true 作为参数调用 evalJSON() 会清理传入的字符串。

    【讨论】:

      【解决方案7】:

      如果你使用jQuery,你也可以使用:

      $.getJSON(url, function(data) { });
      

      然后你可以做类似的事情

      data.key1.something
      data.key1.something_else
      

      等等

      【讨论】:

      • 你在用jQuery,不是吗?
      【解决方案8】:

      只是为了好玩,这里有一种使用函数的方法:

       jsonObject = (new Function('return ' + jsonFormatData))()
      

      【讨论】:

      • 有趣的方法,我不确定我是否将它与可用的 JSON.Parse 一起使用,但很高兴看到有人跳出框框思考。
      • 这与只使用eval 非常相似,并不安全。 :P
      • 这具有使用 eval 的所有缺点,但更复杂,维护者更难理解。
      【解决方案9】:
      $.ajax({
        url: url,
        dataType: 'json',
        data: data,
        success: callback
      });
      

      回调传递返回的数据,该数据将是由 JSON 结构定义并使用 $.parseJSON() 方法解析的 JavaScript 对象或数组。

      【讨论】:

        【解决方案10】:

        使用JSON.parse 可能是最好的方法。

        这是一个例子

        var jsonRes = '{ "students" : [' +
                  '{ "firstName":"Michel" , "lastName":"John" ,"age":18},' +
                  '{ "firstName":"Richard" , "lastName":"Joe","age":20 },' +
                  '{ "firstName":"James" , "lastName":"Henry","age":15 } ]}';
        var studentObject = JSON.parse(jsonRes);
        

        【讨论】:

          【解决方案11】:

          尝试使用此 Data 对象的方法。例如:Data='{result:true,count:1}'

          try {
            eval('var obj=' + Data);
            console.log(obj.count);
          }
          catch(e) {
            console.log(e.message);
          }
          

          您正在使用串行端口编程时,此方法在 Nodejs 中确实很有帮助

          【讨论】:

          • 人们固执于“eval 是邪恶的”真的很有趣,他们会做任何事情来避免它,甚至重写整个 eval 功能..
          • 共识这个技巧是将字符串转换为 JSON 对象的安全方法吗?我可以使用它,因为不需要额外的 js 导入。
          • ANY 使用 evalFunction 的方法同样容易受到攻击
          • undefined; function bye() {...} bye();
          【解决方案12】:

          使用parse()方法最简单的方法:

          var response = '{"result":true,"count":1}';
          var JsonObject= JSON.parse(response);
          

          然后就可以得到JSON元素的值了,例如:

          var myResponseResult = JsonObject.result;
          var myResponseCount = JsonObject.count;
          

          按照jQuery.parseJSON() 文档中的说明使用jQuery

          JSON.parse(jsonString);
          

          【讨论】:

            【解决方案13】:

            我找到了一个“更好”的方法:

            在 CoffeeScript 中:

            try data = JSON.parse(jqxhr.responseText)
            data ||= { message: 'Server error, please retry' }
            

            在 Javascript 中:

            var data;
            
            try {
              data = JSON.parse(jqxhr.responseText);
            } catch (_error) {}
            
            data || (data = {
              message: 'Server error, please retry'
            });
            

            【讨论】:

              【解决方案14】:

              JSON 解析总是很痛苦。如果输入与预期不符,则会引发错误并使您正在做的事情崩溃。

              您可以使用以下小函数来安全地解析您的输入。即使输入无效或已经是在大多数情况下更好的对象,它也总是会转动一个对象:

              JSON.safeParse = function (input, def) {
                // Convert null to empty object
                if (!input) {
                  return def || {};
                } else if (Object.prototype.toString.call(input) === '[object Object]') {
                  return input;
                }
                try {
                  return JSON.parse(input);
                } catch (e) {
                  return def || {};
                }
              };
              

              【讨论】:

              • Object.prototype.toString.call(input) === '[object Object]' 应该是 typeof input === 'object' IMO
              • typeof 输入返回 null 对象和数组。所以这不是安全的方法。
              • 您之前已经介绍过null 的情况,并且数组 一个对象。如果你想测试它,你可以使用instanceof。此外,如果你给这个函数一个Array,它会捕捉到return def,它可以返回完美的数组。
              • 我的评论是关于捕捉物体时的常识。我的函数可以有几个预防措施,但使用 typeof 输入通常不是检测对象的首选方式。
              • IMO,常识不使用toString() 方法来检查变量是否为对象。见AngularJSjQueryUnderscore,甚至devs
              【解决方案15】:
              JSON.parse(jsonString);
              

              json.parse 会变成对象。

              【讨论】:

                【解决方案16】:

                如果我们有这样的字符串:

                "{\"status\":1,\"token\":\"65b4352b2dfc4957a09add0ce5714059\"}"
                

                那么我们可以简单地使用JSON.parse 两次将该字符串转换为JSON 对象:

                var sampleString = "{\"status\":1,\"token\":\"65b4352b2dfc4957a09add0ce5714059\"}"
                var jsonString= JSON.parse(sampleString)
                var jsonObject= JSON.parse(jsonString)
                

                我们可以使用以下方法从 JSON 对象中提取值:

                // instead of last JSON.parse:
                var { status, token } = JSON.parse(jsonString);
                

                结果将是:

                status = 1 and token = 65b4352b2dfc4957a09add0ce5714059
                

                【讨论】:

                  【解决方案17】:

                  JSON.parse() 将传入函数的任何 JSON 字符串转换为 JSON 对象。

                  为了更好地理解它,请按F12在浏览器中打开“Inspect Element”并进入控制台编写以下命令:

                  var response = '{"result":true,"count":1}'; //sample json object(string form)
                  JSON.parse(response); //converts passed string to JSON Object.
                  

                  现在运行命令:

                  console.log(JSON.parse(response));
                  

                  你会得到一个对象{result: true, count: 1}的输出。

                  为了使用那个对象,你可以把它赋值给变量,也许是obj

                  var obj = JSON.parse(response);
                  

                  通过使用 obj 和点 (.) 运算符,您可以访问 JSON 对象的属性。

                  尝试运行命令:

                  console.log(obj.result);
                  

                  【讨论】:

                    【解决方案18】:

                    官方文档

                    JSON.parse() 方法解析 JSON 字符串,构造字符串描述的 JavaScript 值或对象。可以提供一个可选的reviver 函数来在结果对象返回之前对其进行转换。

                    语法:

                    JSON.parse(text[, reviver])
                    

                    参数:

                    text : 要解析为 JSON 的字符串。有关 JSON 语法的说明,请参阅 JSON 对象。

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

                    返回值

                    与给定 JSON 文本对应的 Object。

                    例外情况

                    如果要解析的字符串不是有效的 JSON,则抛出 SyntaxError 异常。

                    【讨论】:

                      【解决方案19】:

                      JSON.parse()解析JSON字符串,数据变成JavaScript对象:

                      JSON.parse(jsonString)
                      

                      这里,JSON代表处理JSON数据集。

                      想象一下我们从网络服务器收到了这个文本:

                      '{ "name":"John", "age":30, "city":"New York"}'
                      

                      解析成 JSON 对象:

                      var obj = JSON.parse('{ "name":"John", "age":30, "city":"New York"}'); 
                      

                      这里 obj 是相应的 JSON 对象,如下所示:

                      { "name":"John", "age":30, "city":"New York"}
                      

                      要获取值,请使用 . 运算符:

                      obj.name // John
                      obj.age //30
                      

                      使用 JSON.stringify() 将 JavaScript 对象转换为字符串。

                      【讨论】:

                        【解决方案20】:

                        将对象转换为 JSON,然后对其进行解析,对我有用,例如:

                        JSON.parse(JSON.stringify(object))
                        

                        【讨论】:

                          【解决方案21】:

                          只是针对不同输入类型的封面解析

                          用 JSON.parse() 解析数据,数据变成 JavaScript 对象。

                          var obj = JSON.parse('{ "name":"John", "age":30, "city":"New York"}');
                          

                          在从数组派生的 JSON 上使用 JSON.parse() 时,该方法将返回 JavaScript 数组,而不是 JavaScript 对象。

                          var myArr = JSON.parse(this.responseText);
                          console.log(myArr[0]);
                          

                          JSON 中不允许使用日期对象。 对于日期做这样的事情

                          var text = '{ "name":"John", "birth":"1986-12-14", "city":"New York"}';
                          var obj = JSON.parse(text);
                          obj.birth = new Date(obj.birth);
                          

                          JSON 中不允许使用函数。 如果需要包含函数,请将其写为字符串。

                          var text = '{ "name":"John", "age":"function () {return 30;}", "city":"New York"}';
                          var obj = JSON.parse(text);
                          obj.age = eval("(" + obj.age + ")");
                          

                          【讨论】:

                            【解决方案22】:

                            老问题,我知道,但是没有人注意到这个解决方案是使用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 );
                                  }
                            

                            这更安全一点,因为它在函数内部执行,而不是直接在您的代码中编译。所以如果里面有函数声明,就不会绑定到默认的window对象。

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

                            【讨论】:

                              【解决方案23】:

                              总结:

                              Javascript(浏览器和 NodeJS)有一个内置的 JSON 对象。在这个对象上有两种方便的方法来处理JSON。它们是:

                              1. JSON.parse()JSON为参数,返回JS对象
                              2. JSON.stringify() 以 JS 对象为参数返回 JSON 对象

                              其他应用:

                              除了非常方便地处理JSON之外,它们还可以用于其他方式。 JSON 两种方法的结合使我们能够非常轻松地对数组或对象进行深度克隆。例如:

                              let arr1 = [1, 2, [3 ,4]];
                              let newArr = arr1.slice();
                              
                              arr1[2][0] = 'changed'; 
                              console.log(newArr); // not a deep clone
                              
                              let arr2 = [1, 2, [3 ,4]];
                              let newArrDeepclone = JSON.parse(JSON.stringify(arr2));
                              
                              arr2[2][0] = 'changed'; 
                              console.log(newArrDeepclone); // A deep clone, values unchanged

                              【讨论】:

                                【解决方案24】:

                                您也可以使用reviver函数进行过滤。

                                var data = JSON.parse(jsonString, function reviver(key, value) {
                                   //your code here to filter
                                });
                                

                                更多信息请阅读JSON.parse

                                【讨论】:

                                  【解决方案25】:

                                  性能

                                  这个问题已经有了很好的答案,但是我对性能很好奇,今天 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
                                  • 对于小的深对象HERE
                                  • 对于大的浅对象HERE
                                  • 对于大深度对象HERE

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

                                  let obj_ShallowSmall = {
                                    field0: false,
                                    field1: true,
                                    field2: 1,
                                    field3: 0,
                                    field4: null,
                                    field5: [],
                                    field6: {},
                                    field7: "text7",
                                    field8: "text8",
                                  }
                                  
                                  let obj_DeepSmall = {
                                    level0: {
                                     level1: {
                                      level2: {
                                       level3: {
                                        level4: {
                                         level5: {
                                          level6: {
                                           level7: {
                                            level8: {
                                             level9: [[[[[[[[[['abc']]]]]]]]]],
                                    }}}}}}}}},
                                  };
                                  
                                  let obj_ShallowBig = Array(1000).fill(0).reduce((a,c,i) => (a['field'+i]=getField(i),a) ,{});
                                  
                                  
                                  let obj_DeepBig = genDeepObject(1000);
                                  
                                  
                                  
                                  // ------------------
                                  // Show objects
                                  // ------------------
                                  
                                  console.log('obj_ShallowSmall:',JSON.stringify(obj_ShallowSmall));
                                  console.log('obj_DeepSmall:',JSON.stringify(obj_DeepSmall));
                                  console.log('obj_ShallowBig:',JSON.stringify(obj_ShallowBig));
                                  console.log('obj_DeepBig:',JSON.stringify(obj_DeepBig));
                                  
                                  
                                  
                                  
                                  // ------------------
                                  // HELPERS
                                  // ------------------
                                  
                                  function getField(k) {
                                    let i=k%10;
                                    if(i==0) return false;
                                    if(i==1) return true;
                                    if(i==2) return k;
                                    if(i==3) return 0;
                                    if(i==4) return null;
                                    if(i==5) return [];
                                    if(i==6) return {};  
                                    if(i>=7) return "text"+k;
                                  }
                                  
                                  function genDeepObject(N) {
                                    // generate: {level0:{level1:{...levelN: {end:[[[...N-times...['abc']...]]] }}}...}}}
                                    let obj={};
                                    let o=obj;
                                    let arr = [];
                                    let a=arr;
                                  
                                    for(let i=0; i<N; i++) {
                                      o['level'+i]={};
                                      o=o['level'+i];
                                      let aa=[];
                                      a.push(aa);
                                      a=aa;
                                    }
                                  
                                    a[0]='abc';
                                    o['end']=arr;
                                    return obj;
                                  }

                                  下面的 sn-p 给出了选择的解决方案

                                  // src: https://stackoverflow.com/q/45015/860099
                                  function A(json) {
                                    return eval("(" + json + ')');
                                  }
                                  
                                  // https://stackoverflow.com/a/26377600/860099
                                  function B(json) {
                                    return (new Function('return ('+json+')'))()
                                  }
                                  
                                  
                                  // improved https://stackoverflow.com/a/26377600/860099
                                  function C(json) {
                                    return Function('return ('+json+')')()
                                  }
                                  
                                  // src: https://stackoverflow.com/a/5686237/860099
                                  function D(json) {
                                    return JSON.parse(json);
                                  }
                                  
                                  // src: https://stackoverflow.com/a/233630/860099
                                  function E(json) {
                                    return $.parseJSON(json)
                                  }
                                  
                                  
                                  
                                   
                                  // --------------------
                                  // TEST
                                  // --------------------
                                  
                                  let json = '{"a":"abc","b":"123","d":[1,2,3],"e":{"a":1,"b":2,"c":3}}';
                                  
                                  [A,B,C,D,E].map(f=> {  
                                    console.log(
                                      f.name + ' ' + JSON.stringify(f(json))
                                    )})
                                  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
                                  This shippet only presents functions used in performance tests - it not perform tests itself!

                                  以下是 chrome 的示例结果

                                  【讨论】:

                                    【解决方案26】:

                                    试试这个。这是用打字稿写的。

                                             export function safeJsonParse(str: string) {
                                                   try {
                                                     return JSON.parse(str);
                                                       } catch (e) {
                                                     return str;
                                                     }
                                               }
                                    

                                    【讨论】:

                                    • 我是 Typescript 的新手。这对JSON.parse() 有什么好处?
                                    • 如果发生任何异常,这将返回输入字符串本身
                                    • @MarcL。据我所知,TypeScript 不会修改 JSON.parse() 和任何其他系统方法(但我没有在这个方向进行研究)
                                    【解决方案27】:
                                    /**
                                     * Safely turning a JSON string into an object
                                     *
                                     * @param {String} str - JSON String
                                     * @returns deserialized object, false if error
                                     */
                                    export function jsonParse(str) {
                                      let data = null;
                                      try {
                                        data = JSON.parse(str);
                                      } catch (err) {
                                        return false;
                                      }
                                      return data;
                                    }
                                    

                                    【讨论】:

                                      猜你喜欢
                                      相关资源
                                      最近更新 更多