3

May need a Javascript language lawyer for this one:

var s1 = "{\"x\":\"y:z\"}"

var o = JSON.parse(s1)
var s2 = JSON.stringify(o)

$('span#s').text(s1);
$('span#s2').text(s2);

if (s1 === s2) {
    $('span#areEqual').text('s1 === s2')
} else {
    $('span#areEqual').text('s1 !== s2')
}

JSON.parse(s2) // okay

$('span#jsonParse').text("JSON.parse(s2) okay")

eval(s2) // bad mojo!

$('span#eval').text("eval(s2) okay")

eval("("+s2+")") // bad mojo, too! 
$('span#eval2').text("eval((s2)) okay")

eval fails on s1, s2, and "("+s2+")".

jsFiddle here.

Jeff Lowery
  • 2,492
  • 2
  • 32
  • 40
  • Generally it is a good practice to avoid `eval()`. `Eval()` can cause serious security issues unless it is used properly. – Lajos Veres Feb 24 '14 at 22:08
  • I don't see any use of `eval()` in your question. – cookie monster Feb 24 '14 at 22:08
  • @cookiemonster see the fiddle – hanleyhansen Feb 24 '14 at 22:09
  • check the jsFiddle link [http://jsfiddle.net/jlowery2663/gSAS3/]. – Jeff Lowery Feb 24 '14 at 22:09
  • @hanleyhansen: It should be here instead of on another site. – cookie monster Feb 24 '14 at 22:09
  • 1
    If you're asking why `eval` gives a SyntaxError, it's because that string doesn't represent a valid JavaScript program, which is what `eval` requres. – cookie monster Feb 24 '14 at 22:11
  • But JSON.parse doesn't? – Jeff Lowery Feb 24 '14 at 22:12
  • Eval can execute arbitrary pieces of code. JSON can only deserialize data. – Benjamin Gruenbaum Feb 24 '14 at 22:12
  • 1
    @JeffLowery: No, `JSON.parse()` is a JSON parser. It accepts valid JSON, and doesn't care if it's a valid JS program. – cookie monster Feb 24 '14 at 22:12
  • My point is: both JSON.parse and eval should accept JSON strings. What appears to be happening is that JSON.parse coerces the string to be valid, but eval doesn't. Not sure that is correct, though. – Jeff Lowery Feb 24 '14 at 22:13
  • @JeffLowery: I get your point, and you're incorrect in the suggestion that `eval` should accept JSON strings. – cookie monster Feb 24 '14 at 22:14
  • @cookiemonster to be fair - `eval("("+jsonData+")")` mostly does a good job. – Benjamin Gruenbaum Feb 24 '14 at 22:15
  • @BenjaminGruenbaum: Yes, but then with the addition of the parens, it's no longer valid JSON. :-) – cookie monster Feb 24 '14 at 22:15
  • so www.json.org is wrong, then? "To convert a JSON text into an object, you can use the eval() function. eval() invokes the JavaScript compiler. Since JSON is a proper subset of JavaScript, the compiler will correctly parse the text and produce an object structure. The text must be wrapped in parens to avoid tripping on an ambiguity in JavaScript's syntax." – Jeff Lowery Feb 24 '14 at 22:17
  • 2
    @JeffLowery: No. ***"The text must be wrapped in parens to avoid tripping on an ambiguity in JavaScript's syntax."*** – cookie monster Feb 24 '14 at 22:18
  • So I updated my example. see above – Jeff Lowery Feb 24 '14 at 22:25
  • 1
    @JeffLowery: Contrary to your claim, `eval("("+s2+")")` does not fail. – Chuck Feb 24 '14 at 22:30
  • 1
    You put the one wrapped in parens below the one that has already failed. The execution stops when an error is thrown. – cookie monster Feb 24 '14 at 22:35
  • Oh, sorry. You are correct, @cookiemonster. – Jeff Lowery Feb 24 '14 at 22:45
  • The ECMAScript spec describes the reason [here](http://es5.github.io/#x12.4) *"An ExpressionStatement cannot start with an opening curly brace because that might make it ambiguous with a Block."* So because the first character is `{`, when it is eval'd, it assumes it's getting a block, but then finds it to be invalid. In other words, it doesn't even consider that it may be an object initializer. Note that there's no such issue with an array structure. – cookie monster Feb 24 '14 at 22:56
  • Thanks. I think if that had been the first response, it would have saved me a lot of typing :-P (Chuck tried, but it was a bit over my head). – Jeff Lowery Feb 24 '14 at 23:15

7 Answers7

6

Your problem is that you mixing two unrelated things.

eval() is built-in javascript function, which main purpose is to interpret string of javascript code (thus make potentional security hole)

JSON.parse() function is for parse JSON string. Although very simmilar, do not make mistake, JSON is not Javascript and there are tiny differences. You should not use eval() for parsing JSON

What are the differences between JSON and JavaScript object?

Community
  • 1
  • 1
apocalypz
  • 1,180
  • 1
  • 8
  • 11
2

$eval is automatically evaluated against a given scope.

For example:

$scope.a = 2;
var result = $scope.$eval('1+1+a');
// result is 4

$parse does not require scope. It takes an expression as a parameter and returns a function. The function can be invoked with an object that can resolve the locals:

For example:

var fn = $parse('1+1+a');
var result = fn({ a: 2 });
// result is 4
Honza Zidek
  • 9,204
  • 4
  • 72
  • 118
1

When you use eval for parsing JSON you need to wrap your expression with parentheses

eval('(' + s2 + ')');

jsfiddle

jcubic
  • 61,973
  • 54
  • 229
  • 402
1

Check out what the specification says about JSON and eval http://www.json.org/js.html Notice this part specifically

The eval function is very fast. However, it can compile and execute any JavaScript program, so there can be security issues. The use of eval is indicated when the source is trusted and competent. It is much safer to use a JSON parser. In web applications over XMLHttpRequest, communication is permitted only to the same origin that provide that page, so it is trusted. But it might not be competent. If the server is not rigorous in its JSON encoding, or if it does not scrupulously validate all of its inputs, then it could deliver invalid JSON text that could be carrying dangerous script. The eval function would execute the script, unleashing its malice.

JSON is just a javascript object, and nothing more. Valid javascript could include functions, execution blocks, etc. If you just eval() a string, it could have code it in. JSON will parse if it's just JSON, but you can't know for sure by just stuffing it into eval. For example

var s = "(function(){ /* do badStuff */ return {s: 123, t: 456}; })()";
var result = eval(s);

Would give you a var result with the contents {s: 123, t: 456} but would also execute any code hidden in your function. If you were taking this input from elsewhere, code could be executing and not actually break anything on your end. Now the same example with JSON.parse

var result = JSON.parse(s);

It throws an error with the message:

Uncaught SyntaxError: Unexpected token ( 

So the parse saves you from remote code execution here, even if someone tried to sneak it in.

Will
  • 2,604
  • 2
  • 18
  • 14
  • Yes, good advice. I was hacking a quick prototype and for a long time now have been relying on JSON directly from $.ajax with JSON type specified. I use eval() hardly ever, but it was the first thing that popped to mind. Bad, bad eval. – Jeff Lowery Feb 24 '14 at 23:14
0

eval wasn't an expression - i've updated it to evaluate eval(s2 === s1); Otherwise it will try & execute what's within the eval & stop execution.

RST_7
  • 86
  • 4
0

eval() attempts to evaluate a block of JavaScript code. If you had created a script file that started with the same text, you would have gotten the same error. In that context, I believe the braces signify a compound statement, as in an if-statement or for-statement body, but at the beginning of the compound statement is a string followed by a colon, which is not valid syntax.

If you wanted a string that would evaluate to an object, you'd have to enclose the object expression in parentheses to make it explicit that it's an expression. But as apocalypz says, you should not attempt to eval JSON. It's wrong on so many levels.

Chuck
  • 234,037
  • 30
  • 302
  • 389
  • I don't disagree with not using eval(), but trying to understand what seems to be conflicting information. What appears to be happening is JSON.parse is scrubbing the input. The escaped quotes tangle up the eval parse because it's seeing something like this: {x:y:z} – Jeff Lowery Feb 24 '14 at 22:34
  • @JeffLowery: No, that isn't what's happening. You'd get the same result with just `"{\"x\":1}"`. I explained what's happening in this answer — it isn't being interpreted as an object expression at all. In order to make it parse as an object expression, you'd need to surround it in parens. – Chuck Feb 24 '14 at 22:34
  • It's not a Javascript object? Looks like one to JSON.parse(): > var s1 = "{\"x\":\"y:z\"}" undefined > var o = JSON.parse(s1) undefined > o Object {x: "y:z"} – Jeff Lowery Feb 24 '14 at 22:38
  • @JeffLowery: Are you here to understand or to argue? Because you seem to be ignoring what people are saying. JavaScript is a superset of JSON. It includes things that are not in JSON. In this case, your string is being interpreted as one of those other things, while JSON interprets it as JSON. It is a valid object expression, but it doesn't parse as one in that context. – Chuck Feb 24 '14 at 22:39
  • I'm not ignoring you, I'm trying to understand. I've been responding with additional information as I do so. – Jeff Lowery Feb 24 '14 at 22:42
  • @JeffLowery: Then why are you quoting me as saying "It's not a JavaScript object"? That isn't what I said at all. I said JavaScript does not parse that syntax as an object expression in that context. It *is* a correct object expression, but sitting alone by itself, it is not parsed that way. That's why you need the parens — to signify to JavaScript, "This is an expression." – Chuck Feb 24 '14 at 22:44
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/48311/discussion-between-jeff-lowery-and-chuck) – Jeff Lowery Feb 24 '14 at 22:47
  • Yes. It's been so long since I tried using eval() that I forgot about the wrapping parentheses. Up to now I haven't really had that explained to me (or I forgot it). – Jeff Lowery Feb 24 '14 at 22:50
0

if you really want to use eval instead of JSON.parse() for parsing JSON then you should write something like

var o2; // declare o2 out of eval in case of "use strict"
eval("o2 = "+s1); // parse s1 and the assignment to the local o2
console.log(o2); // enjoy the local variable :)

...
asdru
  • 1,147
  • 10
  • 19