Trouble understanding jQuery.parseJSON JSON.parse fallback - javascript

This is the source of $.parseJSON
function (data) {
if (typeof data !== "string" || !data) {
return null;
}
// Make sure leading/trailing whitespace is removed (IE can't handle it)
data = jQuery.trim(data);
// Attempt to parse using the native JSON parser first
if (window.JSON && window.JSON.parse) {
return window.JSON.parse(data);
}
// Make sure the incoming data is actual JSON
// Logic borrowed from http://json.org/json2.js
if (rvalidchars.test(data.replace(rvalidescape, "#").replace(rvalidtokens, "]").replace(rvalidbraces, ""))) {
return (new Function("return " + data))();
}
jQuery.error("Invalid JSON: " + data);
}
I have trouble understanding the following fallbacks
return (new Function("return " + data))();
and also ( this one is not in jQuery )
return (eval('('+ data + ')')
I would like to know these things
How this parsing fallback works really?
Why eval is not used in the fallback? (Is it not faster than new Function())

new Function() allows you to pass your function as a string.
In this case, the function is created to simply return the object described by the json string. Since the json is a valid object literal, this function simply returns the object defined in the json. The new function is immediately invoked, returning that object.
As far as performance, some quick googling found claims that new Function() is faster than eval, though I have not tested this myself.

Related

Parsing and evaluating complex objects using JInt

I have a requirement to evaluate certain expressions, and based on the evaluation choose the next steps.
I am trying to use JInt javscript interpreter for this.
For basic evaluations from json, it is working fine. But when I am trying to add additional operations on array, that's where the issue is coming.
My approach,
set value for base objects => write a function to evaluate expressions => invoke this function and pass the expression to evaluate => function will use eval to evaluate passed string. Initial objects are already set in previous steps.
Something like this,
inputStart.Reference.CompanyCode == \"ABC\"
In this, inputStart is the base object which I have already set using
engine.SetValue("inputStart", JsonConvert.DeserializeObject(inputStart));
inputStart is json text. Fiddle has the string as well for reference.
And finally the function,
engine.Execute("function evaluateExpression(expression) { " +
"return eval(expression); " +
"}")
.GetValue("evaluateExpression");
var test = engine.Invoke("evaluateExpression", "inputStart.Reference.CompanyCode == \"ABC\"");
This code works correctly. Now the variation is, I have another json text which has array in it.
And I am trying to use filter or find to locate specific array item, and then have some operation on it.
Something like this,
inputQuestions.Questions.filter((x) => x.Code == \"INIT_IMP\")[0].Answer.Answer.Code
This gives me below error,
Unhandled exception. System.ArgumentException: Accessed JArray values
with invalid key value: "filter". Int32 array index expected.
My complete program for reference,
string inputStart = "{\n\t\"PurposeCode\": \"START\",\n\t\"Reference\": {\n\t\t\"CompanyCode\": \"ABC\"\t\t\n\t}\n}";
string inputQuestions = "{\n\t\"QuestionSetCode\": \"QUES_SET_1\",\n\t\"Version\": 1,\n\t\"Questions\": [{\n\t\t\"Code\": \"INIT_IMP\",\n\t\t\"Text\": \"Impact ?\",\n\t\t\"AnswerType\": 3,\n\t\t\"SequenceNumber\": 1,\n\t\t\"Answer\": {\n\t\t\t\"Answers\": [{\n\t\t\t\t\"Code\": \"INIT_IMP_DO\",\n\t\t\t\t\"Text\": \"A\",\n\t\t\t\t\"SequenceNumber\": 1\n\t\t\t}, {\n\t\t\t\t\"Code\": \"INIT_IMP_DA\",\n\t\t\t\t\"Text\": \"B\",\n\t\t\t\t\"SequenceNumber\": 2\n\t\t\t}, {\n\t\t\t\t\"Code\": \"INIT_IMP_AO\",\n\t\t\t\t\"Text\": \"C\",\n\t\t\t\t\"SequenceNumber\": 3\n\t\t\t}],\n\t\t\t\"Answer\": {\n\t\t\t\t\"Code\": \"INIT_IMP_DO\",\n\t\t\t\t\"Text\": \"A\",\n\t\t\t\t\"SequenceNumber\": 1\n\t\t\t},\n\t\t\t\"Type\": \"ListOfValuesAnswer\"\n\t\t},\n\t\t\"Applicable\": true\n\t}, {\n\t\t\"Code\": \"ENT_ACC_LIQ\",\n\t\t\"Text\": \"Exposure ?\",\n\t\t\"AnswerType\": 3,\n\t\t\"SequenceNumber\": 2,\n\t\t\"Answer\": {\n\t\t\t\"Answers\": [{\n\t\t\t\t\"Code\": \"ENT_ACC_LIQ_Y\",\n\t\t\t\t\"Text\": \"Yes\",\n\t\t\t\t\"SequenceNumber\": 1\n\t\t\t}, {\n\t\t\t\t\"Code\": \"ENT_ACC_LIQ_N\",\n\t\t\t\t\"Text\": \"No\",\n\t\t\t\t\"SequenceNumber\": 2\n\t\t\t}],\n\t\t\t\"Answer\": {\n\t\t\t\t\"Code\": \"ENT_ACC_LIQ_N\",\n\t\t\t\t\"Text\": \"No\",\n\t\t\t\t\"SequenceNumber\": 2\n\t\t\t},\n\t\t\t\"Type\": \"ListOfValuesAnswer\"\n\t\t},\n\t\t\"Applicable\": true\n\t}],\n\t\"ClientId\": null\n}";
Engine engine = new Engine();
engine.SetValue("inputStart", JsonConvert.DeserializeObject(inputStart));
engine.SetValue("inputQuestions", JsonConvert.DeserializeObject(inputQuestions));
engine.Execute("function evaluateExpression(expression) { " +
"return eval(expression); " +
"}")
.GetValue("evaluateExpression");
var test = engine.Invoke("evaluateExpression", "inputQuestions.Questions.filter((x) => x.Code == \"INIT_IMP\")[0].Answer.Answer.Code == \"INIT_IMP_DO\" && inputStart.Reference.CompanyCode == \"ABC\"");
Console.WriteLine(test);
Here is the complete fiddle for what I am trying to do, https://dotnetfiddle.net/GtamM8
If anyone has come across such issue, or can suggest a solution to this, it will be very helpful.
Because you are using the new language constructs, you should be using 3.x preview release from NuGet. 3.x series contains support for lambda expressions and array filtering against them.

Am I being over-complicated for a parser?

This is more a design question that a technical question, but since there is a tag called design-patterns I think it makes sense.
My scenario is as follows: I have a CSV file, that I want to use to populate an object and then use that object for making an API call. So far so good.
The tricky part is that the CSV data could require some conversions before populating the object, and once the object is created and populated I want to edit some of those object properties.
The view and controller operations are going to be provided by angular and I have focused on the template stuff.
How is the object composed? Well, my idea is as follows:
The result will be a hasmap of objects, where each object has a collection of fields which are also objects. This object represents a row, and each field a column.
Each field will have a value property, this way angular can do it's two way data binding magic. It will have also a set of methods for querying and interacting with the object, like asking for the object class (ng-dirty if it is required and empty), adding another element if it is a collection instead of a single value, and that kind of things.
To build this object I will proceed this way:
First build a set of rules for parsing each column/field data.
Each rule has a parser method that parses the data, and a constructor method, which returns the "field" object I mentioned above.
The constructor calls the parser, which will parse the data and return a valid representation of that data. The constructor will then return an object field with the parser result as value and a set of method for interacting with that object.
Rules are created using the ruleFactory function. This function receives the "type" of the field, if it is required (mandatory) or not, the constructor to use and an object for additional options such the parser to use and returns a rule.
Some constructors are built on top of other constructors. For example, the basic constructor is used by the arrayConstructor, that extends the object the basic constructor returns and then returns it. The arrayConstructor is used by the limitedValuesConstructor and so on. If you did not noticed, some constructors falls back to a default parser if it is not defined.
My idea wish is that this is extensible, so if in the future if the format of the data changes, I just add another parser, or I update an existing parser. Same stuff for the constructors.
But, I feel that I'm going to complicated for this task, and I want your opinion about it. Another thing that scares me a lot is that I feel that this does not fit under any valid design pattern that I know. It is not prototypical, not classical, not a module pattern (closure) and I'm not familiar with composition, but I feel that this is not valid composition neither.
Below you can find the basic code inside a closure.
(function () {
function ruleFactory (type,required,constructor,options) {
constructor = constructor || basicConstructor;
var column = {
'type': type,
'required': required || false,
'parser': options && options.parser ,
'constructor': function (data) { return constructor.apply(column,[data,required,options])}
};
return column
}
function strParser (data){
if( typeof data === "string" ){
return data
} else if( data && data.toString){
return data.toString();
}
return ''
}
function arrParser (data){
if(!data){
return []
}
if( angular.isArray(data) ){
return data
}
return strParser(data).split(' ');
}
function basicConstructor (data,required) {
var parser = this.parser || strParser; //fallback to the most basic parser
var column = {};
column.value = parser(data);
column.getClass = function (){
if( required && ! this.value ){
return 'ng-dirty'
}
return ''
};
return column
}
function arrConstructor (data,required) {
this.parser = this.parser || arrParser; // default parser for this constructor
var column = basicConstructor.apply(this,arguments);
column.addValue = function (value) {
column.value.push(value);
}
column.getClass = function ( ) {
if ( column.value.length <= 0 && required ) {
return 'ng-dirty'
}
return ''
}
return column
}
})()
The "rules" object (or array or whatever you want) is built like this:
var rules = {
"someColName": ruleFactory('number',true), // true means required. Defaults to false
"someList" : ruleFactory('array',false,arrConstructor), //No parser defined, so fallbacks to default parser for arrConstructor
"otherList" : ruleFactory('array',false,arrConstructor, {parser:arrParser}) }
Any opinion / constructive criticism is very welcome.
Regards

How to run JavaScript code represented by a string?

Through AJAX I receive random string (built at RUNTIME on the server) that contain some JavaScript code like:
Plugins.add('test', function()
{
return
{
html: '<div>test</div>',//EDITED
width: 200
}
});//EDITED
In the client I want to be able to execute this code. I tried using eval function like this
eval("(" + str + ")");
but I get error. I removed all "\r\n" and removed the last ";"(semicolon) and after that eval function succeeded. But, If I add some comment to the code above, eval fails.
How I can run the code from the string?
Just remove those parenthesis:
eval(str);
asssuming that you made a typo in your question and your server is sending the missing end parenthesis and comma within the object:
Plugins.add('test', function()
{
return {
html: '<div>test</div>', // <-- comma was missing
width: 200
};
}
); // <-- was missing
Note that eval() is considered "evil" as it is very dangerous.
new Function(str)()
or for JSON:
new Function('return ' + str)();
If it happens to fit your needs any better than eval. It's still evil like eval.
You are missing a comma in your object literal. Return on its own line will simply drop out of the function. I assume you want to return the object. You need to specify the return value on the same line.
Plugins.add('test', function() {
var ret = {
html: '<div>test</div>',
width: 200,
}
return ret
};
You could return your string with a content type of "text/javascript" or "application/x-javascript" - the return value will be executed as JavaScript as soon as it is returned.

How can I speed up this bit of JSON date parsing?

I am stuck using an AJAX library from about 5 years ago in this project, and it had some issues with parsing dates in JSON. I wound up rewriting its parse function to use a single regex:
return eval('(' + (enableDateParsing ? text.replace(/"(?:\\)?\/Date\((.*?)\)(?:\\)?\/"/g, "new Date($1)") : text) + ')');
This works really well, but I thought I could get a speed up if I used native JSON parsing in IE8 / chrome / ff, so I added this bit:
if (typeof JSON !== 'undefined' && typeof JSON.parse !== 'undefined') {
var nativeJsonDateParseRegex = /\/Date\(.*?\)\//g;
return JSON.parse(text, function (key, value) {
if (AjaxPro.enableDateParsing && typeof value === 'string' && value.match(nativeJsonDateParseRegex))
{
value = new Date(parseInt(value.substr(6)));
}
return value;
});
}
else // revert to eval for ie6/ie7
The reviver callback will execute once for each JSON property returned, so it has to be very fast. During a profile I've seen it's been called 170484 times, but still runs pretty fast (131.237ms). Any ideas on how to make it faster, or is this the best you can do without serious tweaking?
Your code contains a lot of constant conditions, you'll be fine with checking once whether native JSON is supported or not.
Suggestions:
check for native JSPN support at page load, and add the right function accordingly.
Drop the global flag from the regex if you do not need it
Drop regular expressions if possible, if every date always starts with "/Date(", search for it. It's much faster (see benchmark at jsperf.com)
todo: check whether parseInt can be replaced with an other method to get rid of the trailing )/.
If AjaxPro.enableDateParsing is a constant, you can remove if from AjaxPro.jsonParse and and make it a condition like the check for native JSON
Code without RE:
if (typeof JSON !== 'undefined' && typeof JSON.parse !== 'undefined') {
AjaxPro.nativeJsonDateParseRegex = /\/Date\(.*?\)\//g;
AjaxPro.dateFunc = function(key, value) {
if (typeof value === "string" && !value.indexOf("/Date(")) {
return new Date(value.substring(6, value.length-2));
}
return value;
};
AjaxPro.jsonParse = function(text) {
if (AjaxPro.enableDateParsing) {
return JSON.parse(text, AjaxPro.dateFunc);
}
return JSON.parse(text);
};
} else // revert to eval for ie6/ie7
This should be highly optimized. You might want to run some more test on your own in multiple browsers. Maybe checking for a property of a string is faster than checking its type (doubt it), thing like that.
One not so good microoptimization, but still worth giving a try.
Since your substring contains millisecond timestamp only, and no other garbage string.
You can remove the call to parseInt.
You can try typecasting with simple mathematical operation like multiplication with 1.
Might save some time if you are too keen on microoptimizations.
value = new Date(1*(value.substr(6)));
example:
a = "a:3333";
b = a.substring(2);
alert(b*2); // alerts 6666

How to check null objects in jQuery

I'm using jQuery and I want to check the existence of an element in my page. I have written following code, but it's not working:
if($("#btext" + i) != null) {
//alert($("#btext" + i).text());
$("#btext" + i).text("Branch " + i);
}
How do I check the existence of the element?
Check the jQuery FAQ...
You can use the length property of the jQuery collection returned by your selector:
if ( $('#myDiv').length ){}
(Since I don't seem to have enough reputation to vote down the answer...)
Wolf wrote:
Calling length property on undefined
or a null object will cause IE and
webkit browsers to fail!
Instead try this:
// NOTE!! THE FOLLOWING IS WRONG; DO NOT USE! -- EleotleCram
if($("#something") !== null){
// do something
}
or
// NOTE!! THE FOLLOWING IS WRONG; DO NOT USE! -- EleotleCram
if($("#something") === null){
// don't do something
}
While it is true that calling the length property on an undefined or null object will cause browsers to fail, the result of jQuery's selectors (the $('...')) will never be null or undefined. Thus the code suggestions make no sense. Use one of the other answers, they make more sense.
(Update 2012) Because people look at code and this answer is pretty high up the list: For the last couple of years, I have been using this small plugin:
jQuery.fn['any'] = function() {
return (this.length > 0);
};
I think $('div').any() reads better than $('div').length, plus you won't suffer as much from typos: $('div').ayn() will give a runtime error, $('div').lenght will silently most likely always be falsy.
__
Edits november 2012:
1) Because people tend to look at code and not read what is said around the code, I added two big caveat lector notes to the quoted code of Wolf.
2) I added code of the small plugin I use for this situation.
The lookup function returns an array of matching elements. You could check if the length is zero. Note the change to only look up the elements once and reuse the results as needed.
var elem = $("#btext" + i);
if (elem.length != 0) {
elem.text("Branch " + i);
}
Also, have you tried just using the text function -- if no element exists, it will do nothing.
$("#btext" + i).text("Branch " + i);
jquery $() function always return non null value - mean elements matched you selector cretaria. If the element was not found it will return an empty array.
So your code will look something like this -
if ($("#btext" + i).length){
//alert($("#btext" + i).text());
$("#btext" + i).text("Branch " + i);
}
In jQuery 1.4 you get the $.isEmptyObject function, but if you are forced to use an older version of jQ like us poor Drupal developers just steal use this code:
// This is a function similar to the jQuery 1.4 $.isEmptyObject.
function isObjectEmpty(obj) {
for ( var name in obj ) {
return false;
}
return true;
}
Use it like:
console.log(isObjectEmpty(the_object)); // Returns true or false.
What about using "undefined"?
if (value != undefined){ // do stuff }
no matter what you selection is the function $() always returns a jQuery object so that cant be used to test. The best way yet (if not the only) is to use the size() function or the native length property as explained above.
if ( $('selector').size() ) {...}
if ( $('#whatever')[0] ) {...}
The jQuery object which is returned by all native jQuery methods is NOT an array, it is an object with many properties; one of them being a "length" property. You can also check for size() or get(0) or get() - 'get(0)' works the same as accessing the first element, i.e. $(elem)[0]
use $("#selector").get(0) to check with null like that. get returns the dom element, until then you re dealing with an array, where you need to check the length property. I personally don't like length check for null handling, it confuses me for some reason :)
Using the length property you can do this.
jQuery.fn.exists = function(){return ($(this).length < 0);}
if ($(selector).exists()) {
//do somthing
}
when the object is empty return this error:
Uncaught TypeError: Cannot read property '0' of null
I try this code :
try{
if ($("#btext" + i).length) {};
}catch(err){
if ($("#btext" + i).length) {
//working this code if the item, not NULL
}
}
if (typeof($("#btext" + i)) == 'object'){
$("#btext" + i).text("Branch " + i);
}
Calling length property on undefined or a null object will cause IE and webkit browsers to fail!
Instead try this:
if($("#something") !== null){
// do something
}
or
if($("#something") === null){
// don't do something
}

Categories

Resources