安全地将JSON字符串转换为对象

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

显然,我可以不安全地这样做:

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

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

1413869 次浏览

我不确定其他方法可以做到这一点,但这是你在原型(JSON教程)中如何做到这一点。

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

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

这个答案适用于IE<7,对于现代浏览器,请查看上面的Jonathan答案。

这个答案已经过时了,乔纳森在上面的答案(#0)现在是最好的答案.

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

如果您使用jQuery,您还可以使用:

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

然后你可以做这样的事情

data.key1.somethingdata.key1.something_else

$.ajax({url: url,dataType: 'json',data: data,success: callback});

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

jQuery方法现在已弃用。改用此方法:

let jsonObject = JSON.parse(jsonString);

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

如果您使用jQuery,请使用:

jQuery.parseJSON( jsonString );

这正是你想要的(参见jQuery留档)。

#0是一种纯JavaScript方法,只要你能保证一个相当现代的浏览器。

这似乎是问题所在:

通过Ajax webSocket等接收的输入,它将采用String格式,但您需要知道它是否为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.parse()”中的简单代码示例:

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

并扭转它:

var str = JSON.stringify(arr);

尝试将该方法与此Data对象一起使用。例如:Data='{result:true,count:1}'

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

这种方法在Nodejs中确实有帮助,当您正在使用串行端口编程

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

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

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

在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'});

使用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);

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

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

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

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

按照#0留档中的描述使用jQuery

JSON.parse(jsonString);

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

为了更好地理解它,按F12在浏览器中打开“检查元素”,然后转到控制台编写以下命令:

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));

您将获得作为Object{result: true, count: 1}的输出。

为了使用该Object,您可以将其分配给变量,也许是obj

var obj = JSON.parse(response);

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

尝试运行命令:

console.log(obj.result);
JSON.parse(jsonString);

json.parse将变成对象。

JSON解析总是很痛苦。如果输入不如预期,它会抛出错误并使您正在做的事情崩溃。

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

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

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

JSON.parse(JSON.stringify(object))

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

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

有关更多信息,请阅读#0

官方留档

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

语法:

JSON.parse(text[, reviver])

参数:

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

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

返回值

与给定JSON文本对应的Object。

例外

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

老问题,我知道,但是没有人注意到这个解决方案使用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元素的配置设置(例如数据属性)简单快捷。

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

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

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

JSON.parse(jsonString)

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

假设我们从Web服务器收到以下文本:

'{ "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 // Johnobj.age //30

将JavaScript对象转换为带有JSON.stringify()的字符串。

总结:

Javascript(浏览器和NodeJS)都有一个内置的JSON对象。在这个对象上有2种处理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

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

"{\"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

只是为了不同输入类型的封面解析

用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中不允许日期对象。#36825;做这样的事情

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 + ")");

性能

这个问题已经有了很好的答案,但我对性能很好奇,今天2020.09.21我在Chromev85、Safariv13.1.2和Firefox v80上对MacO HighSierra 10.13.6进行了测试,以选择解决方案。

搜索结果

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

输入图片描述

详情

我执行4个测试用例:

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

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;}

下面的代码片段提供了选择的解决方案

// src: https://stackoverflow.com/q/45015/860099function A(json) {return eval("(" + json + ')');}
// https://stackoverflow.com/a/26377600/860099function B(json) {return (new Function('return ('+json+')'))()}

// improved https://stackoverflow.com/a/26377600/860099function C(json) {return Function('return ('+json+')')()}
// src: https://stackoverflow.com/a/5686237/860099function D(json) {return JSON.parse(json);}
// src: https://stackoverflow.com/a/233630/860099function 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的示例结果

输入图片描述

另一种选择

const json = '{ "fruit": "pineapple", "fingers": 10 }'let j0s,j1s,j2s,j3sconsole.log(`{ "${j0s="fruit"}": "${j1s="pineapple"}", "${j2s="fingers"}": ${j3s="10"} }`)

在JavaScript中解析JSON的推荐方法是使用JSON.parse()

背景

#0 API是与ECMAScript 5一起引入的,此后按市场份额计算已在>99%的浏览器中实施。

jQuery曾经有一个#0函数,但它在jQuery 3.0中被弃用了。无论如何,在很长一段时间里,它只不过是JSON.parse()的包装器。

示例

const json = '{ "city": "Boston", "population": 500000 }';const object = JSON.parse(json);console.log(object.city, object.population);


浏览器兼容性

所有主流浏览器都支持JSON.parse吗?

基本上,是的(见参考)。