Safely turning a JSON string into an object

Viewed 1402620

Given a string of JSON data, how can I safely turn that string into a JavaScript object?

Obviously I can do this unsafely with something like:

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

but that leaves me vulnerable to the JSON string containing other code, which it seems very dangerous to simply eval.

29 Answers

This answer is for IE < 7, for modern browsers check Jonathan's answer above.

This answer is outdated and Jonathan's answer above (JSON.parse(jsonString)) is now the best answer.

JSON.org has JSON parsers for many languages including four different ones for JavaScript. I believe most people would consider json2.js their goto implementation.

I'm not sure about other ways to do it but here's how you do it in Prototype (JSON tutorial).

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

Calling evalJSON() with true as the argument sanitizes the incoming string.

If you're using jQuery, you can also use:

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

Then you can do things like

data.key1.something
data.key1.something_else

etc.

Parse the JSON string with JSON.parse(), and the data becomes a JavaScript object:

JSON.parse(jsonString)

Here, JSON represents to process JSON dataset.

Imagine we received this text from a web server:

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

To parse into a JSON object:

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

Here obj is the respective JSON object which looks like:

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

To fetch a value use the . operator:

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

Convert a JavaScript object into a string with JSON.stringify().

If we have a string like this:

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

then we can simply use JSON.parse twice to convert this string to a JSON object:

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

And we can extract values from the JSON object using:

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

The result will be:

status = 1 and token = 65b4352b2dfc4957a09add0ce5714059

Performance

There are already good answer for this question, but I was curious about performance and today 2020.09.21 I conduct tests on MacOs HighSierra 10.13.6 on Chrome v85, Safari v13.1.2 and Firefox v80 for chosen solutions.

Results

  • eval/Function (A,B,C) approach is fast on Chrome (but for big-deep object N=1000 they crash: "maximum stack call exceed)
  • eval (A) is fast/medium fast on all browsers
  • JSON.parse (D,E) are fastest on Safari and Firefox

enter image description here

Details

I perform 4 tests cases:

  • for small shallow object HERE
  • for small deep object HERE
  • for big shallow object HERE
  • for big deep object HERE

Object used in above tests came from 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;
}

Below snippet presents chosen solutions

// 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!

And here are example results for chrome

enter image description here

Summary:

Javascript (both browser and NodeJS) have a built in JSON object. On this Object are 2 convenient methods for dealing with JSON. They are the following:

  1. JSON.parse() Takes JSON as argument, returns JS object
  2. JSON.stringify() Takes JS object as argument returns JSON object

Other applications:

Besides for very conveniently dealing with JSON they have can be used for other means. The combination of both JSON methods allows us to make very easy make deep clones of arrays or objects. For example:

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

Just to the cover parse for different input types

Parse the data with JSON.parse(), and the data becomes a JavaScript object.

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

When using the JSON.parse() on a JSON derived from an array, the method will return a JavaScript array, instead of a JavaScript object.

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

Date objects are not allowed in JSON. For Dates do somthing like this

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

Functions are not allowed in JSON. If you need to include a function, write it as a string.

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

Another option

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

The recommended approach to parse JSON in JavaScript is to use JSON.parse()

Background

The JSON API was introduced with ECMAScript 5 and has since been implemented in >99% of browsers by market share.

jQuery once had a $.parseJSON() function, but it was deprecated with jQuery 3.0. In any case, for a long time, it was nothing more than a wrapper around JSON.parse().

Example

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


Browser Compatibility

Is JSON.parse supported by all major browsers?

Pretty much, yes (see reference).

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

Try this. This one is written in typescript.

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