Weird JSON parsing behavior in js, "Unexpected token :" - javascript

As demonstrated in this jsfiddle, if you have a JS file and you create a JSON object without using it, it behaves differently depending on whether the keys(members) are wrapped in quotes or not.
valid code:{ a: 1};
invalid code: { "a": 1 };
What you will get is an error message (in Chrome, different for FF/IE, but still fails on syntax)
Uncaught SyntaxError: Unexpected token :
but if you use the object in some way, for example: alert({ "a": 1 }); everything is OK again.
Why does this happen?

The statement:
{ a: 1 };
is not an object literal. It's a block statement with one labeled expression in it. It's valid.
This:
{ "a": 1 };
is a syntax error because it's just not parseable. The quoted "a" starts an expression statement inside the block, but then the next token after the string is a colon, and there's no expression form that looks like an expression followed by a colon.
Now:
var x = { "a": 1 };
works because the "{" is not interpreted as the start of a block statement. That statement starts with var, so it's a variable declaration. Within the expression on the right side of the "=" token, the only thing that a "{" can mean is the start of an object literal. Similarly, note that:
({ "a": 1 });
is OK because the opening parenthesis makes the parser expect a nested subexpression, so again the "{" unambiguously means that it's the start of an object literal.

I just realized than when loading the JSON via require and the filename does not end on .json i get this error. Renaming the file to bla.json and it works fine.

This error can popup when doing a jQuery AJAX call using jsonp when jsonp is not necessary. Try switching your data type on your AJAX call if this is the case to normal json
$.ajax({
dataType: 'json', // try using json rather than json p
...
});

Related

What difference does it makes to parse something as an object literal rather than as a block?

Sorry for my ignorance on JavaScript basic concepts.
It boils down to this:
Literal - A value found directly in the script. Examples:
3.14
"This is a string"
[2, 4, 6]
Expression - A group of tokens, often literals or identifiers, combined
with operators that can be evaluated to a specific value. Examples:
2.0
"This is a string"
(x + 2) * 4
There is a very clear difference b/w the above two in Javascript.
I happen to read this article. And I am familiar with the difference b/w function declaration & function expression and when to use one over other or vice-versa.
From the same article:
....You might also recall that when evaluating JSON with eval, the string
is usually wrapped with parenthesis — eval('(' + json + ')'). This is
of course done for the same reason — grouping operator, which
parenthesis are, forces JSON brackets to be parsed as expression
rather than as a block:
try {
{ "x": 5 }; // "{" and "}" are parsed as a block
} catch(err) {
// SyntaxError
}
({ "x": 5 }); // grouping operator forces "{" and "}" to be parsed as object literal
So, what difference does it make to parse something as an object literal other than parsing them as a block?
And for what purpose should I consider to make use of grouping character, in context of parsing?
First, don't eval JSON, use JSON.parse on the String source
A block is a "group of expressions" for example,
let x = 0;
if (true) {
// this is a block
++x;
}
However, equally this is also a block
let x = 0;
{ // hi there, I'm a block!
++x;
}
This means when the interpreter sees block notation, it assumes a block even if you do something like this
{ // this is treated as a block
foo: ++x
}
Here, foo acts as a label rather than a property name and if you try to do more complex things with the attempted object literal, you're going to get a Syntax Error.
When you want to write an Object literal ambiguously like this, the solution is to force the interpreter into "expression mode" explicitly by providing parenthesis
({ // this is definately an Object literal
foo: ++x
})
A group that begins with a { and ends with a } is treated as either object literal or a block depending on context*.
Within an expression context the group is interpreted as an object literal. Writing a block within an expression context will generate a syntax error:
// Valid code:
foo = {a:b};
({a:b});
// Syntax errors:
foo = {var a = b};
({var a = b});
Outside of an expression context the group is interpreted as a block. Depending on exactly how the code is written, an object literal written outside of an expression context is either a syntax error or will be interpreted as a label.
*note: In the ECMAscript spec the word "context" is used to mean something specific. My use of the word here is with the general meaning in computer science with regards to parsing.

JSON Parsing in AngularJS Service

I have Angular service and in that service I have a code:
this.testMethod = function( ) {
this.jsonTest = "[{'id':'123','title':'XYZ','id2':'456','status':2}]";
this.parseTest(this.jsonTest);
};
this.parseTest = function(jsonTest) {
var jsonTestObj = JSON.parse(jsonTest); // I get error hear
}
Test method is getting called on ng-click event.
Error that I am getting while testing in latest chrome browser:
SyntaxError: Unexpected token ' at Object.parse (native)
......
I tried multiple things to fix it but nothing seems to work.
Every time I get undefined value error.
Basically I want to parse JSON object and get the values.
What am I doing wrong?
Use this
this.jsonTest = '[{"id":"123","title":"XYZ","id2":"456","status": "2"}]';
Or this
this.jsonTest = "[{\"id\":\"123\",\"title\":\"XYZ\",\"id2\":\"456\",\"status\": \"2\"}]";
You either need to use ' outside or " outside and escape the quotes inside.
Both the key and value need to be in double quotes.
You need to use double quotes in your string, other than that your code should work.
To quote from specification
A value can be a string in double quotes, or a number, or true or false or null, or an object or an array. These structures can be nested.
Your JSON is not valid. Always check your JSON integrity in those cases, i personaly use
http://jsonlint.com/

eval is evil, but is it flawed? [duplicate]

This question already has answers here:
Why does JavaScript's eval need parentheses to eval JSON data?
(7 answers)
Closed 7 years ago.
If I run this:
eval('{ear: {"<=": 6}}');
I get an error:
Uncaught SyntaxError: Unexpected token :
Let's create the object manually:
var foo = {};
foo.ear = {};
foo.ear["<="] = 6;
Now, the following code:
JSON.stringify(foo)
Returns the following string:
'{"ear":{"<=":6}}'
The same string as the one I started with (except the white characters, but those are irrelevant), so eval(JSON.stringify(foo)) returns the same syntax error error message. However:
$.parseJSON(JSON.stringify(foo))
is executed correctly. What is the reason of that?
EDIT:
As nnnnnn and Ron Dadon pointed out, the initial string and the result of stringify are different. However, as I pointed out in the question, even the result of stringify used as input for eval will result in the syntax error message.
EDIT2:
Based on the answers and experiments conducted, this function is interesting:
function evalJSON(text) {
return eval("(" + text + ")");
}
Main {} are parsed as block statement.
try to wrap in parenthesis:
eval('({ear: {"<=": 6}})');
In javascript {} can be parsed as a block or an object
examples:
//object
var user = {
name: "John",
age: "32"
};
//block
{
let a = 5;
console.log(a);
}
//object:
var a = {};
console.log({});
return {};
({});
//block:
function(){}
for(k in o){}
{}
Object literal notations need to be evaluated. This happens when you assign a variable:
var a = {ear: {"<=": 6}};
or when you put parentheses around it, a anonymous object:
({ear: {"<=": 6}});
Otherwise curly brackets are parsed as block markers. In your case this means {ear:...} is a label definition, the label is named ear. The next block {"<=": 6} gives you a syntax error because "<=": 6 is invalid syntax.
The same applies if you put this into an eval statement.
It's not flawed. To understand what is happening you need to understand what kind of statements are seen (left to right) by the parser.
A simple way to get into it is to play around with a Javascript AST Visualizer
You will get the same exception with much simpler {"b":4}. It's parsed as "b":4 inside a block. That's not valid javascript. No AST tree for you...
However it's due to an exception inside of a {} statement. That's a BlockStatement. AST tree:
A similar {b:4} would be understood as b:4, a valid js statement - a b label for 4... That's parsed as
Lastly, a ({b:4}) would be understood as an object declaration with a b property equal to 4. That's parsed as
ECMAScript 2015
On Blocks:
Block : { StatementList }
On eval itself:
Eval creates a new Realm, which is parsed (several steps here) as a sequence of Statements ( a StatementList ), which in turn this section has BlockStatement as a first option. This must start with { (see above), so if you wrap it with a bracket (({})) it cannot be a BlockStatement... But if it matches as BlockStatement it must be a BlockStatement.
A side note in section on Expressions:
An ExpressionStatement cannot start with a U+007B (LEFT CURLY BRACKET) because that might make it ambiguous with a Block

Difference between JSON.parse() and eval()

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.
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?
$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
When you use eval for parsing JSON you need to wrap your expression with parentheses
eval('(' + s2 + ')');
jsfiddle
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.
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.
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.
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 :)
...

JavaScript syntax madness - anyone knows the background why this happens

I am kinda lost on why the following code does what it does the way it does.
For the following expression I would expect 1 as result, since on the right side of a literal we actually have an obj.
Expression:
> { a : 1 }.a
result Chrome:
Syntax error: Unexpect token .
result NodeJS
1
(Another question: why nodejs and chrome differs on this (and on the following))
While the expression itself has a syntax error assigning it to a variable still works.
> var x = { a : 1 }.a; x;
result:
1
Now using eval around the expression does work in Chrome and in NodeJS
> eval({ a : 1 }.a)
result Chrome and Node
1
Now using eval and the string based expression neither platform works
eval("{ a : 1 }.a")
result Chrome:
SyntaxError: Unexpected token >
result NodeJS:
... //REPL waits more code
And finally the parenthesis solves it all but why?
> eval("({ a : 1 }.a)")
Result:
Works everywhere
eval takes a string, so calling eval({ a : 1 }.a) is the same as eval("1").
In statement context, {} delimit a block, not an object literal. You can go in expression context by using () as a grouping operator.
So:
{ a : 1 }.a
Is actually a block, label, numeric literal and dot-access on nothing:
{
a: 1
}
.a
Node REPL actually runs in an expression context to begin with, but that's unusual. They run your code like eval("(" + replInput + ")")
Try ({ a : 1 }).a
This will also work everywhere as the code in round bracket is executed first and then .a of that object is extracted.
`{ a: 1 }`
is just a block and doesn't return anything for the . operator to work on but enclosing it in () does return an object of which .a is extracted

Categories