The Curious Schemer

The following sentence is false. The preceding sentence is true.

Why Won’t eval() Eval My JSON? (Or: JSON Object !== Object Literal)

with 28 comments

I’ve been reviewing a lot of code lately, and I found so many abuses of eval(), that I wanted to blog about it. But I found that Eric Lippert has done excellent short articles about it right here, and here, and the abuses he blogged about there are exactly the ones I keep seeing in the code I’ve been reviewing. So instead, I’ll blog about something that baffled me a few months back, when eval() simply wouldn’t work when I used it to evaluate my JSON string.

The scenario was simple: we were getting back JSON strings that represent complex objects from the server side. Most of them were arrays that contained objects, and it worked perfectly, like demonstrated in my favourite tool jrunscript below:

js> var jsonEmployees = "[ { name: 'Ray', role: 'Fixer' }, { name: 'Rayzor', role: 'Cutter' } ]";
js> var employees = eval(jsonEmployees);
js> employees.length
2.0
js> employees[0].name
Ray
js> employees[0].role
Fixer

However when the JSON string is not an array, but an object, like this, it barfs:

js> var jsonEmployee = "{ name: 'Ray', role: 'Fixer' }";
js> var employee = eval(jsonEmployee);
script error: sun.org.mozilla.javascript.internal.EcmaError: SyntaxError: missing ; before statement (<STDIN>#1(eval)#1) in <STDIN>#1(eval) at line number 1

What happened? It says it’s missing a “;”. So maybe we need a ; at the end of the object literal:

js> var jsonEmployee = "{ name: 'Ray', role: 'Fixer' };";
js> var employee = eval(jsonEmployee);
script error: sun.org.mozilla.javascript.internal.EcmaError: SyntaxError: missing ; before statement (<STDIN>#1(eval)#1) in <STDIN>#1(eval) at line number 1

Hmmm. Obviously not. But why? I checked and rechecked the JSON syntax in the JSON site, it seemed to be correct. Putting the object inside an array (i.e.: “[{ name: 'Ray', role: 'Fixer' }]“) always works, but why shouldn’t eval() eval what seems to be a perfectly valid JSON syntax? (Actually, strictly speaking, no, it is not a valid JSON syntax although it is valid JavaScript syntax–I realized this later when I incorporated json.js into my app. But that’s not why eval() failed. Keep on reading.)

Ugh, Spec! Expressions! Ambiguity!

Have you ever read a language spec? I hate language specs (especially C++’s). But the answer to our question is buried deep somewhere inside the ECMAScript standard, 3rd edition, section 12.4, about Expression Statement: “Note that an ExpressionStatement cannot start with an opening curly brace because that might make it ambiguous with a Block.

Ahhh, so when we do this:

js> var jsonEmployee = "{ name: 'Ray', role: 'Fixer' };";

eval() gets confused, because it is interpreting the “{” as the beginning of a block instead of an object literal! Fortunately, there are a few ways to get around this. The easiest one is to add parentheses around the expression. That is:

js> var jsonEmployee = "({ name: 'Ray', role: 'Fixer' })";
js> var employee = eval(jsonEmployee);
js> employee.name + ", " + employee.role
Ray, Fixer

Alternatively, we can make it obvious to eval() that the expression is an object initializer, by assigning it to a variable within the eval string:

js> var jsonEmployee = "var employee = { name: 'Ray', role: 'Fixer' };";
js> eval(jsonEmployee);
js> employee.name + ", " + employee.role
Ray, Fixer

A Better But Picky eval() That Won’t Evaluate My Object Literals

eval() works well when used appropriately. For instance, for the case of my app, using eval() is fine, not evil, because we’re only using it to parse JSON strings that come from a trusted server (that is, our own server). It’s different when the string you’re eval()-ing (JSON or otherwise) comes from some untrusted source. In that case, it’s better to use a real JSON parser.

(Remember that JSON is a subset of JavaScript–a JSON parser only parses JSON, and not the rest of JavaScript. Whereas eval() parses everything. That said, JSON parser is obviously slower since it has to check that only valid JSON can be eval()-ed.)

Using the JSON parser is simple. You don’t even need to add parentheses to get the string parsed. Just download Douglas Crockford’s JSON JavaScript library here, include it in your page (or load it if you’re running in jrunscript), and you’re set.

Note something about this parser though: it is REALLY strict. It won’t parse perfectly OK object literals like { name: “Ray”, age: 31 }. Eval parses this without problems:

js> var jsonRay = "{ name: 'Ray', age: 31 }";
js> var ray = eval("(" + jsonRay + ")");
js> ray.name
Ray
js> ray.age
31.0

But when we use the JSON library, only JSON-compliant strings are accepted. Let’s try it using jrunscript:

js> load("C:\\Documents and Settings\\schemer\\My Documents\\json.js")
js> var jsonRay = "{ name: 'Ray', age: 31 }";
js> var ray = jsonRay.parseJSON();
script error: sun.org.mozilla.javascript.internal.JavaScriptException: [object Error] (C:\Documents and Settings\schemer\My Documents\json.js#270) in C:\Documents and Settings\schemer\My Documents\json.js at line number 270

This string: “{ name: ‘Ray’, age: 31 }” is not valid JSON! Check the specification again. An object has to look like this to be JSON-compliant (properties names have to be quoted, and with double quotations marks too):

{ "name": "Ray", "age": 31 }

And indeed, now the string is parsed correctly into an object. Note that unlike eval(), we don’t need to put parentheses around the object initialiser–the parseJSON() method does it for us:

js> var jsonRay = "{ \"name\": \"Ray\", \"age\": 31 }";
js> var ray = jsonRay.parseJSON();
js> ray.name
Ray
js> ray.age
31.0

The fact that the new json.js adds parseJSON() as a method of String (instead of the old approach of passing the string to JSON.parse()) changes the way you code, because obviously you can’t call parseJSON() on a null or undefined variable. This JSON library also adds toJSONString() method to JavaScript datatypes, so we can do this:

js> ray.toJSONString()
{"age":31,"name":"Ray"}

The Conclusion

Use eval() when you can trust the string. Use parseJSON() otherwise. (I know, I know, it’s a lousy conclusion, like a fizzle after a pop or something. But I’m getting real sleepy, kay? Need to get some sleep.)

About these ads

Written by rayfd

March 28, 2007 at 6:13 pm

Posted in JavaScript, Technology

28 Responses

Subscribe to comments with RSS.

  1. Indeed, this ambiguity is irksome. I talked about this ambiguity a bit in the context of automatic semicolon insertion in the comments here:

    http://blogs.msdn.com/ericlippert/archive/2004/02/02/66334.aspx

    Eric Lippert

    March 28, 2007 at 6:23 pm

  2. Ah, thanks Eric! Excellent blog you have there–been going through your posts :)

    rayfd

    March 28, 2007 at 6:34 pm

  3. Great Blog!! Thanks!

    Siddharth

    April 8, 2007 at 11:40 pm

  4. thaaaaanks.. You saved my day, this problem made me crazy for a couple of hours.

    Joe

    March 20, 2008 at 7:00 am

  5. Thank youuuuuuuuuuuuuu!!!!!!!!!!!!!

    Narine

    April 18, 2008 at 5:26 am

  6. Excellent! Thanks

    Wole

    May 4, 2008 at 11:30 pm

  7. You just saved my day! Been messing with the “missing ; before statement” bug for half a day, finding this solved it. Thanks a bunch!

    casperbp

    May 5, 2008 at 1:53 pm

  8. [...] funkcji eval() do parsowania obiektu JSON który otrzymałem z API otrzymywałem Parse Error. Wyjaśnienie tego problemu znalazłem, ale zajęło mi to dość dużo czasu, zatem warto się nim podzielić [...]

  9. THANK YOU

    MAnuel

    June 5, 2008 at 4:19 pm

  10. Nice Introductory Article. Inspired by this article I wrote a fully functiional PHP-JSON-Ajax Demo
    Please have a look
    http://www.codemator.com/2008/10/19/using-php-and-json-for-ajax-response/

    codemator

    October 19, 2008 at 7:11 pm

  11. Thanks for taking the time to post this – I had been chasing the same problem for hours

    Stuart Crsobie

    November 15, 2008 at 1:42 pm

  12. Yeah, it’s strict when parsing JSON data. you got to make sure the JSON data is well formatted. May touch up a bit when using eval()

    http://en.wikipedia.org/wiki/JSON

    Boris

    January 8, 2009 at 9:21 am

  13. Thanks — this was driving my crazy — the parens worked great.

    CMS

    June 4, 2009 at 2:05 pm

  14. Awesome tip! Works like a charm :)

    michael

    July 10, 2009 at 4:29 pm

  15. Thats a cool analysis, i have been through it and i know how irritating it is to use eval() even strange “{‘name':’tom’}” doesnt work for JSON.parse() looks like json parser looks for literals after literals and not use the eval() mehthod at all.

    sammy cool

    February 3, 2010 at 4:37 pm

  16. I’m glad someone had the patience to go through the spec… I tried your suggestion to wrap the object in brackets and it worked perfectly. Thanks!

    Tim Farland

    July 27, 2010 at 9:26 am

  17. if look the Douglas Crockford’s JSON JavaScript library, at the bottom you will see the line j = eval(‘(‘ + text + ‘)’);
    Every javascript library with JSON support, uses the eval function.

    anon

    August 27, 2010 at 12:18 am

  18. Thanks a lot, i’ve been rackin’ my brain with this problem for 2 days. Thanks for reading the spec!, I think I wouldn’t have tried it by myself :p

    Juan Capristán

    October 15, 2010 at 9:57 am

  19. Thanks Curious Schemer!
    This was a very helpful bit of advice. Appreciate your having taken the time to write it up and share it with the rest of us. -Robert

    Robert

    October 17, 2010 at 3:13 am

  20. How to pass a json object to other javascript file…

    Munish

    May 27, 2011 at 8:29 am

  21. i am new to birt(business intelligence and reporting technique)used in eclipse… how can i bind json data with birt chart or report.. i tried alot to search the same on net but dint get anything… all examples are of high level.. i need simple example of binding json with birt chart…….. see can u help me out

    Munish

    May 27, 2011 at 11:14 am

  22. Thanks so much, that solved that little headache :)

    Adams

    June 13, 2011 at 10:26 pm

  23. Thank you! There are some times I hate JavaScript…

    Anonymous

    March 30, 2012 at 9:42 am

  24. does eval() works fine in IE6 too ??

    dinker

    March 4, 2013 at 7:34 pm

    • No it doesn’t work on IE6.

      iamjayram

      September 2, 2013 at 12:24 pm

  25. Nice blog. I have been fetching about the limitations of eval() and one of the best programmers (Arun) referred me to this blog. I couldn’t stop myself posting a (“Nice blog “) comment.

    Jayram

    September 2, 2013 at 12:22 pm


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: