8

I am being sent an ill formed JSON string from a third party. I tried using JSON.parse(str) to parse it into a JavaScript object but it of course failed.

The reason being is that the keys are not strings:

{min: 100}

As opposed to valid JSON string (which parses just fine):

{"min": 100}

I need to accept the ill formed string for now. I imagine forgetting to properly quote keys is a common mistake. Is there a good way to change this to a valid JSON string so that I can parse it? For now I may have to parse character by character and try and form an object, which sounds awful.

Ideas?

Mogsdad
  • 44,709
  • 21
  • 151
  • 275
lostintranslation
  • 23,756
  • 50
  • 159
  • 262
  • It looks like there are some solutions here: http://stackoverflow.com/questions/4210160/safely-parsing-a-json-string-with-unquoted-keys?rq=1 – showdev Apr 19 '13 at 17:59
  • I think using a Function constructor should work in this case : `var data = new Function('return '+illFormedJSON;)()` – m90 Apr 19 '13 at 18:00
  • 1
    `(?:[a-z]+):+` I'd say search for all non proper keys. This is not a complete regex for should give you a place to start. Don't have time to fully answer the question. – travis Apr 19 '13 at 18:02
  • @m90 Isn't that eval in disguise? How about a JSON like: `{a: alert('whoa!')}` – UltraInstinct Apr 19 '13 at 18:13
  • @Thrustmaster The OP said the **keys** aren't quoted, not the values. Either way, you're right, it's a special case of `eval` that would execute any Javascript in the main string – Ian Apr 19 '13 at 18:17
  • That's pretty true, but I don't know the exact circumstances of this and when the OP's saying that he has to deal with malformed JSON, you'll have to find some kind of workaround that is semi-ideal. If this is coming from a "trustworthy" but malformed API I wouldn't want to use RegExp-parsing to somehow mangle the malformed strings. If this is dealing with user input it'd be a no-go, that's for sure. – m90 Apr 19 '13 at 18:20
  • There is no such thing as malformed JSON. If it's malformed, it is not JSON. – Niet the Dark Absol Apr 19 '13 at 18:58
  • 1
    Wow thanks for the insight Kolink. Ever write code that doesn't compile.....its not really code. Didn't really think the title, 'Cannot parse not JSON into JS object would get many hits.' You know what I meant. – lostintranslation Apr 19 '13 at 21:19
  • BTW, https://github.com/daepark/JSOL works great. Try something {a: alert('whoa!')}. Handles it nicely, just as JSON lib does. – lostintranslation Apr 22 '13 at 13:52

3 Answers3

5

You could just eval, but that would be bad security practice if you don't trust the source. Better solution would be to either modify the string manually to quote the keys or use a tool someone else has written that does this for you (check out https://github.com/daepark/JSOL written by daepark).

huwiler
  • 915
  • 9
  • 9
2

I did this just recently, using Uglifyjs to evaluate:

var jsp = require("uglify-js").parser;
var pro = require("uglify-js").uglify;

var orig_code = "var myobject = " + badJSONobject;
var ast = jsp.parse(orig_code); // parse code and get the initial AST
var final_code = pro.gen_code(ast); // regenerate code

$('head').append('<script>' + final_code + '; console.log(JSON.stringify(myobject));</script>');

This is really sloppy in a way, and has all the same problems as an eval() based solution, but if you just need to parse/reformat the data one time, then the above should get you a clean JSON copy of the JS object.

BishopZ
  • 6,269
  • 8
  • 45
  • 58
  • 1
    FWIW, this will be one of the slowest ways of doing it. Uglify is a full blown JavaScript parser; manipulating the string yourself (e.g. http://stackoverflow.com/a/16110830/444991) will be *much* more efficient. – Matt Apr 19 '13 at 19:02
  • Yes, this approach was designed for a Grunt-style build script, rather than a realtime application where efficiency is a concern. – BishopZ Apr 21 '13 at 22:46
1

Depending on what else is in the JSON, you could simply do a string replace and replace '{' with '{"' and ':' with '":'.

Paul
  • 3,725
  • 12
  • 50
  • 86