Parsing "relaxed" JSON without eval

JavascriptJson

Javascript Problem Overview


What is the easiest method to parse "relaxed" JSON but avoid evil eval?

The following throws an error:

JSON.parse("{muh: 2}");

since proper JSON should have keys quoted: {"muh": 2}


My use case is a simple test interface I use to write JSON commands to my node server. So far I simply used eval as it's just a test application anyway. However, using JSHint on the whole project keeps bugging me about that eval. So I'd like a safe alternative that still allows relaxed syntax for keys.

PS: I don't want to write a parser myself just for the sake of the test application :-)

Javascript Solutions


Solution 1 - Javascript

You could sanitize the JSON using a regular expression replace:

var badJson = "{muh: 2}";
var correctJson = badJson.replace(/(['"])?([a-z0-9A-Z_]+)(['"])?:/g, '"$2": ');
JSON.parse(correctJson);

Solution 2 - Javascript

You already know this, since you referred me here, but I figure it might be good to document it here:

I'd long had the same desire to be able to write "relaxed" JSON that was still valid JS, so I took Douglas Crockford's eval-free json_parse.js and extended it to support ES5 features:

https://github.com/aseemk/json5

This module is available on npm and can be used as a drop-in replacement for the native JSON.parse() method. (Its stringify() outputs regular JSON.)

Hope this helps!

Solution 3 - Javascript

This is what I ended up having to do. I extended @ArnaudWeil's answer and added support for having : appear in the values:

var badJSON = '{one : "1:1", two : { three: \'3:3\' }}';

var fixedJSON = badJSON

	// Replace ":" with "@colon@" if it's between double-quotes
	.replace(/:\s*"([^"]*)"/g, function(match, p1) {
		return ': "' + p1.replace(/:/g, '@colon@') + '"';
	})

	// Replace ":" with "@colon@" if it's between single-quotes
	.replace(/:\s*'([^']*)'/g, function(match, p1) {
		return ': "' + p1.replace(/:/g, '@colon@') + '"';
	})

	// Add double-quotes around any tokens before the remaining ":"
	.replace(/(['"])?([a-z0-9A-Z_]+)(['"])?\s*:/g, '"$2": ')

	// Turn "@colon@" back into ":"
	.replace(/@colon@/g, ':')
;

console.log('Before: ' + badJSON);
console.log('After: ' + fixedJSON);
console.log(JSON.parse(fixedJSON));

It produces this output:

Before: {one : "1:1", two : { three: '3:3' }}
After: {"one":  "1:1", "two":  { "three":  "3:3" }}
{
  "one": "1:1",
  "two": {
    "three": "3:3"
  }
}

Solution 4 - Javascript

If you can't quote keys when writing the string, you can insert quotes before using JSON.parse-

var s= "{muh: 2,mah:3,moh:4}";
s= s.replace(/([a-z][^:]*)(?=\s*:)/g, '"$1"');

var o= JSON.parse(s);
/*  returned value:[object Object] */
JSON.stringify(o)
/*  returned value: (String){
	"muh":2, "mah":3, "moh":4
}

Solution 5 - Javascript

You can also use Flamenco's really-relaxed-json (https://www.npmjs.com/package/really-relaxed-json) that goes a step further and allows no commas, dangling commas, comments, multiline strings, etc.

Here's the specification http://www.relaxedjson.org

And some online parsers:
http://www.relaxedjson.org/docs/converter.html

Preloaded with the 'bad json'

{one : "1:1", two : { three: '3:3' }}

Bad JSON

Preloaded with even 'worse json' (no commas)

{one : '1:1' two : { three: '3:3' }}

Worse JSON

Preloaded with 'terrible json' (no commas, no quotes, and escaped colons)

{one : 1\:1 two : {three : 3\:3}}

Terrible JSON

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionaxkibeView Question on Stackoverflow
Solution 1 - JavascriptArnaud WeilView Answer on Stackoverflow
Solution 2 - JavascriptAseem KishoreView Answer on Stackoverflow
Solution 3 - JavascriptMalvineousView Answer on Stackoverflow
Solution 4 - JavascriptkennebecView Answer on Stackoverflow
Solution 5 - JavascriptSteven SpunginView Answer on Stackoverflow