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

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
js> employees[0].name
js> employees[0].role

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
js> ray.age

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
js> ray.age

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()

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.)

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

  1. 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.

  2. 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.

  3. 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

  4. 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

  5. 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.

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 )

Google photo

You are commenting using your Google 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 )

Connecting to %s