167

How can I convert a string that describes an object into a JSON string using JavaScript (or jQuery)?

e.g: Convert this (NOT a valid JSON string):

var str = "{ hello: 'world', places: ['Africa', 'America', 'Asia', 'Australia'] }"

into this:

str = '{ "hello": "world", "places": ["Africa", "America", "Asia", "Australia"] }'

I would love to avoid using eval() if possible.

BuZZ-dEE
  • 6,075
  • 12
  • 66
  • 96
snorpey
  • 2,327
  • 3
  • 18
  • 28
  • Why is your string not valid JSON in the first place? How are you generating that? – gen_Eric Jan 27 '12 at 16:28
  • 2
    The string is stored in a `data`-attrubute, like this: `
    ` and I don't want to use single quotes in the HTML(so it is probably not to be trusted)
    – snorpey Jan 27 '12 at 16:41
  • 5
    @snorpey: `
    ` is 100% valid HTML (what does single quotes have to do with trusting it or not?). If you do it this way, you can just `JSON.parse` it and it'll work fine. **Note:** the keys need to be quoted too.
    – gen_Eric Jan 27 '12 at 16:42
  • @Rocket thanks for your efforts! I just wanted to find a way around having to use single quotes in HTML (even though it is 100% valid) and JSON notation. – snorpey Jan 27 '12 at 17:05
  • @snorpey: They way around is not to put JSON in a HTML attribute in the first place. I guess you *could* use double quotes, and escape the ones in the JSON `
    `. If you don't want to use valid JSON in the attribute, then you're gonna have to make your own format and parse it yourself.
    – gen_Eric Jan 27 '12 at 17:06
  • These are just tips instead of answer. - You can use [http://www.jsoneditoronline.org/](http://www.jsoneditoronline.org/) to verify my JSON. - While passing JSON string from code behind mindful of adding appropriate escape characters. – user669915 Nov 04 '16 at 17:41

20 Answers20

184

If the string is from a trusted source, you could use eval then JSON.stringify the result. Like this:

var str = "{ hello: 'world', places: ['Africa', 'America', 'Asia', 'Australia'] }";
var json = JSON.stringify(eval("(" + str + ")"));

Note that when you eval an object literal, it has to be wrapped in parentheses, otherwise the braces are parsed as a block instead of an object.

I also agree with the comments under the question that it would be much better to just encode the object in valid JSON to begin with and avoid having to parse, encode, then presumably parse it again. HTML supports single-quoted attributes (just be sure to HTML-encode any single quotes inside strings).

Matthew Crumley
  • 101,441
  • 24
  • 103
  • 129
  • this does not make sense, if string is from trusted source, why we convert it instead we make it as valid json. – allenhwkim Jan 02 '14 at 17:42
  • 2
    @allenhwkim The idea is to convert from invalid JSON to valid JSON, so `eval` converts the string to a JavaScript object (which works, as long as the string represents valid JavaScript, even if it's not valid JSON). Then `JSON.stringify` converts from an object back to a (valid) JSON string. Calling `eval` is dangerous if the string is not from a trusted source because it could literally run any JavaScript which opens up the possibility of cross-site scripting attacks. – Matthew Crumley Jan 04 '14 at 20:13
  • 2
    eval will still do bad things in this case if the string is constructed, for example, like this: var str = "(function() {console.log(\"bad\")})()"; – Rondo Jan 31 '15 at 01:40
  • Using eval() will execute JS code. It can be easily abused. – FisNaN Feb 02 '18 at 11:52
  • @allenhwkim: we never trust any source. Trust in IT means check, check and check again. – Laszlo Varga May 04 '18 at 07:27
  • SyntaxError: Unexpected token { – Urasquirrel Jul 29 '19 at 00:24
112

Your string is not valid JSON, so JSON.parse (or jQuery's $.parseJSON) won't work.

One way would be to use eval to "parse" the "invalid" JSON, and then stringify it to "convert" it to valid JSON.

var str = "{ hello: 'world', places: ['Africa', 'America', 'Asia', 'Australia'] }"
str = JSON.stringify(eval('('+str+')'));

I suggest instead of trying to "fix" your invalid JSON, you start with valid JSON in the first place. How is str being generated, it should be fixed there, before it's generated, not after.

EDIT: You said (in the comments) this string is stored in a data attribute:

<div data-object="{hello:'world'}"></div>

I suggest you fix it here, so it can just be JSON.parsed. First, both they keys and values need to be quoted in double quotes. It should look like (single quoted attributes in HTML are valid):

<div data-object='{"hello":"world"}'></div>

Now, you can just use JSON.parse (or jQuery's $.parseJSON).

var str = '{"hello":"world"}';
var obj = JSON.parse(str);
gen_Eric
  • 223,194
  • 41
  • 299
  • 337
50

jQuery.parseJSON

str = jQuery.parseJSON(str)

Edit. This is provided you have a valid JSON string

43

Use simple code in the link below :

http://msdn.microsoft.com/es-es/library/ie/cc836466%28v=vs.94%29.aspx

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

and reverse

var str = JSON.stringify(arr);
AlexB
  • 7,302
  • 12
  • 56
  • 74
Ronald Coarite
  • 4,460
  • 27
  • 31
29

I hope this little function converts invalid JSON string to valid one.

function JSONize(str) {
  return str
    // wrap keys without quote with valid double quote
    .replace(/([\$\w]+)\s*:/g, function(_, $1){return '"'+$1+'":'})    
    // replacing single quote wrapped ones to double quote 
    .replace(/'([^']+)'/g, function(_, $1){return '"'+$1+'"'})         
}

Result

let invalidJSON = "{ hello: 'world',foo:1,  bar  : '2', foo1: 1, _bar : 2, $2: 3, 'xxx': 5, \"fuz\": 4, places: ['Africa', 'America', 'Asia', 'Australia'] }"
JSON.parse(invalidJSON) 
//Result: Uncaught SyntaxError: Unexpected token h VM1058:2
JSON.parse(JSONize(invalidJSON)) 
//Result: Object {hello: "world", foo: 1, bar: "2", foo1: 1, _bar: 2…}
Chukwuemeka Maduekwe
  • 6,687
  • 5
  • 44
  • 67
allenhwkim
  • 27,270
  • 18
  • 89
  • 122
9

Use with caution (because of eval()):

function strToJson(str) {
  eval("var x = " + str + ";");
  return JSON.stringify(x);
}

call as:

var str = "{ hello: 'world', places: ['Africa', 'America', 'Asia', 'Australia'] }";
alert( strToJson(str) );
Tomalak
  • 332,285
  • 67
  • 532
  • 628
  • 3
    To the anonymous down-voter. I challenge you to provide a better solution. Apart from that a reason for the down-vote would be nice. – Tomalak Jan 27 '12 at 16:22
  • Why was this down-voted? Yes, `eval` is bad, but his string is not valid JSON, so he can't use `JSON.parse`. – gen_Eric Jan 27 '12 at 16:23
  • 1
    @Rocket: You are wrong. a) `eval()` is the only way to do it. b) I cautioned the OP about it. c) Look at Matthew Crumley's answer and think of a better explanation. (Oh, and d) the statement `eval()` is bad is nonsense in this generalized form.) – Tomalak Jan 27 '12 at 16:24
  • I'm wrong for agreeing with you? That comment was asking why this was-voted. I was explaining why `eval` has to be used here. Did you see *my* answer? – gen_Eric Jan 27 '12 at 16:26
  • 2
    @Rocket: Ah, misunderstanding right there. Sorry, I thought the down-vote was yours. :) – Tomalak Jan 27 '12 at 16:28
  • @Tomalak I've down-voted you. You're right that `eval()` is the only way, but your solution doesn't work – you forget add round brackets. See my post, or post from @Matthew Crumley – kuboslav Jan 27 '12 at 16:31
  • 1
    @kuboslav: It works fine, did you even test it? He's doing `eval("var x = " + str + ";")` which is totally valid JS. You don't need to do `var x = ({a:12})`. – gen_Eric Jan 27 '12 at 16:32
  • @Rocket Yes, I've tested it – it doesn't work. See my post before answer. – kuboslav Jan 27 '12 at 16:33
  • @kuboslav: Then you're testing it wrong, because it works fine. http://jsfiddle.net/ExHWk/ – gen_Eric Jan 27 '12 at 16:34
  • @Rocket – Again: _Note that when you `eval` an object literal, it has to be wrapped in parentheses, otherwise the braces are parsed as a block instead of an object_ – kuboslav Jan 27 '12 at 16:37
  • 1
    @kuboslav: Again: He's not `eval`ing an *object literal*. He's `eval`ing a line of code that sets a variable to an object. I'll ask you again, **Did you test it?** It works just fine: http://jsfiddle.net/ExHWk/ – gen_Eric Jan 27 '12 at 16:39
  • 1
    @kuboslav I usually test things before I post them here. Still there might be errors in them or they might not work everywhere. I'd appreciate a comment if that's the case. An uncommented downvote is not helpful at all - not for me and not for anybody else who might wonder. (Apart from that, I'm not into down-voting. I leave just a comment even under dead-wrong answers. But maybe that's just me.) – Tomalak Jan 27 '12 at 16:40
  • @Tomalak I'm sorry, but I write slowly on notebook keyboard. In future I'll first write comment and then would down-vote. I've down-voted because it doesn't work in my IE7. – kuboslav Jan 27 '12 at 16:46
  • 2
    @kuboslav It does not work in IE7 because IE7 does not have native JSON support. It will start to work as soon as you use [`json2.js`](https://github.com/douglascrockford/JSON-js/blob/master/json2.js). Don't be so trigger-happy. – Tomalak Jan 27 '12 at 16:50
  • @kuboslav: Why are you still using IE7, it's 2012, get with the times :-P (P.S. It doesn't work in IE7 because it doesn't have native `JSON`, nothing to do with the `eval`. You can use Crockford's [JSON shim](https://raw.github.com/douglascrockford/JSON-js/master/json2.js) to add JSON support to IE7.) – gen_Eric Jan 27 '12 at 16:55
  • 1
    @Tomalak you can utilize the onclick attribute of a dummy element to accomplish the same thing, so eval() isn't the _only_ way (technically speaking). I left an answer to show how this alternative method works in practice. – csuwldcat Sep 29 '13 at 16:17
  • @csu Isn't that method using `eval()` as well, under the hood? I would not be surprised. – Tomalak Sep 29 '13 at 18:08
  • 1
    @Tomalak if we want to be uber technical, eval() is a call for the JS engine to parse and run script content, and every textual bit of code (onclick, etc) or script tag invokes the same basic mechanism that backs eval(). The one benefit textual script tags, attributes, and new Function creation can provide, is they are generally easier to optimize (once cast, and cached) for reuse. Constantly using eval to reevaluate the same textual code strings is much harder to optimize in the same way. As the OP asked for no explicit use of eval(), I felt the solution I provided fit the bill. – csuwldcat Sep 29 '13 at 19:53
4

I put my answer for someone who are interested in this old thread.

I created the HTML5 data-* parser for jQuery plugin and demo which convert a malformed JSON string into a JavaScript object without using eval().

It can pass the HTML5 data-* attributes bellow:

<div data-object='{"hello":"world"}'></div>
<div data-object="{hello:'world'}"></div>
<div data-object="hello:world"></div>

into the object:

{
    hello: "world"
}
tokkonopapa
  • 167
  • 4
4

Disclaimer: don't try this at home, or for anything that requires other devs taking you seriously:

JSON.stringify(eval('(' + str + ')'));

There, I did it.
Try not to do it tho, eval is BAD for you. As told above, use Crockford's JSON shim for older browsers (IE7 and under)

This method requires your string to be valid javascript, which will be converted to a javascript object that can then be serialized to JSON.

edit: fixed as Rocket suggested.

gonchuki
  • 4,064
  • 22
  • 33
  • It should be `JSON.stringify(eval('('+str+')'));`, not that I condone `eval`, but his string isn't valid JSON so `JSON.parse` doesn't work. – gen_Eric Jan 27 '12 at 16:20
3

You need to use "eval" then JSON.stringify then JSON.parse to the result.

 var errorString= "{ hello: 'world', places: ['Africa', 'America', 'Asia', 'Australia'] }";
 var jsonValidString = JSON.stringify(eval("(" + errorString+ ")"));
 var JSONObj=JSON.parse(jsonValidString);

enter image description here

Negi Rox
  • 3,828
  • 1
  • 11
  • 18
3

Using new Function() is better than eval, but still should only be used with safe input.

const parseJSON = obj => Function('"use strict";return (' + obj + ')')();

console.log(parseJSON("{a:(4-1), b:function(){}, c:new Date()}"))
// outputs: Object { a: 3, b: b(), c: Date 2019-06-05T09:55:11.777Z }

Sources: MDN, 2ality

SamGoody
  • 13,758
  • 9
  • 81
  • 91
2

There's a much simpler way to accomplish this feat, just hijack the onclick attribute of a dummy element to force a return of your string as a JavaScript object:

var jsonify = (function(div){
  return function(json){
    div.setAttribute('onclick', 'this.__json__ = ' + json);
    div.click();
    return div.__json__;
  }
})(document.createElement('div'));

// Let's say you had a string like '{ one: 1 }' (malformed, a key without quotes)
// jsonify('{ one: 1 }') will output a good ol' JS object ;)

Here's a demo: http://codepen.io/csuwldcat/pen/dfzsu (open your console)

csuwldcat
  • 8,021
  • 2
  • 37
  • 32
2

Your best and safest bet would be JSON5 – JSON for Humans. It is created specifically for that use case.

const result = JSON5.parse("{ hello: 'world', places: ['Africa', 'America', 'Asia', 'Australia'] }");

console.log(JSON.stringify(result));
<script src="https://cdnjs.cloudflare.com/ajax/libs/json5/0.5.1/json5.min.js"></script>
dereli
  • 1,814
  • 15
  • 23
2

Douglas Crockford has a converter, but I'm not sure it will help with bad JSON to good JSON.

https://github.com/douglascrockford/JSON-js

Seth
  • 6,240
  • 3
  • 28
  • 44
1

You have to write round brackets, because without them eval will consider code inside curly brackets as block of commands.

var i = eval("({ hello: 'world', places: ['Africa', 'America', 'Asia', 'Australia'] })");
kuboslav
  • 1,430
  • 5
  • 16
  • 31
0

For your simple example above, you can do this using 2 simple regex replaces:

var str = "{ hello: 'world', places: ['Africa', 'America', 'Asia', 'Australia'] }";
str.replace(/(\w+):/g, '"$1":').replace(/'/g, '"');
 => '{ "hello": "world", "places": ["Africa", "America", "Asia", "Australia"] }'

Big caveat: This naive approach assumes that the object has no strings containing a ' or : character. For example, I can't think of a good way to convert the following object-string to JSON without using eval:

"{ hello: 'world', places: [\"America: The Progressive's Nightmare\"] }"
Topher Hunt
  • 4,404
  • 2
  • 27
  • 51
0

Just for the quirks of it, you can convert your string via babel-standalone

var str = "{ hello: 'world', places: ['Africa', 'America', 'Asia', 'Australia'] }";

function toJSON() {
  return {
    visitor: {
      Identifier(path) {
        path.node.name = '"' + path.node.name + '"'
      },
      StringLiteral(path) {
        delete path.node.extra
      }
    }
  }
}
Babel.registerPlugin('toJSON', toJSON);
var parsed = Babel.transform('(' + str + ')', {
  plugins: ['toJSON']
});
var json = parsed.code.slice(1, -2)
console.log(JSON.parse(json))
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
Moritz Roessler
  • 8,542
  • 26
  • 51
0

var str = "{ hello: 'world', places: ['Africa', 'America', 'Asia', 'Australia'] }" var fStr = str .replace(/([A-z]*)(:)/g, '"$1":') .replace(/'/g, "\"")

console.log(JSON.parse(fStr))enter image description here

Sorry I am on my phone, here is a pic.

Francis Leigh
  • 1,870
  • 10
  • 25
0

A solution with one regex and not using eval:

str.replace(/([\s\S]*?)(')(.+?)(')([\s\S]*?)/g, "$1\"$3\"$5")

This I believe should work for multiple lines and all possible occurrences (/g flag) of single-quote 'string' replaced with double-quote "string".

Chaitanya P
  • 120
  • 7
0
var str = "{ hello: 'world', places: ['Africa', 'America', 'Asia', 'Australia'] }";
var json = JSON.stringify(eval("(" + str + ")"));
Abdul Rasheed
  • 6,486
  • 4
  • 32
  • 48
-1

Maybe you have to try this:

str = jQuery.parseJSON(str)
Jay Hyber
  • 321
  • 1
  • 4
  • 18