JavaScript - Save object with methods as a string - javascript

I've been looking around for a way to do this but can't seem to find anything, I have different configuration objects that I need to save as a text in variables for some processing later on, here is a sample:
object:
args.config.config = {
next: null,
final:[],
delimiter: '~', header: false,
step: function (row) {
var item = {
'line_code': row.data[0][0],
'order': row.data[0][1]
}
args.config.config.final.push(item);
},
complete: function (result) {
console.log('Reading data completed. Processing.');
return args.config.config.next(null, args.config.config.final);
},
error: function () {
console.log('There was an error parsing');
}'
}
I need to save this as a string, so something like:
args.config.config = "{object goes here}";
Without putting everything on one giant line or adding break line characters as this will be parsed later to be used in a config, and that will mess things up, any ideas?
UPDATE:
So changing them into text may not be the best solution, these configs will be stored in a mongo database, so it may take them as is (I have not tried it yet).
One of the other problems I was running into was that in the config object I had this:
final.push(item)
and
return next(null, final)
Which will be defined in another file using the config object:
other file:
exports.parse = function(args, next){//next is what I need to call in the config
var final = []; //this is the final referred to in the config object
....
Baby.parse(data, args.config)
}
So the return next(null, final) and final.push(result) have to refer the the var / function in the new file, but I have no idea how to get that to work, that't why I had to add a final array in the config object and a null next function, then assign it like so:
exports.parse = function(args, next){
args.config.next = next;
....
Baby.parse(data, args.config)
}
the object was calling it with the ugly line:
return args.config.config.next(null, args.config.config.final);
If anyone has a way around this, it would be much appreciated.

If you use JSON.stringify with a "replacer" function and
JSON.parse with a "reviver" function along with new Function(), you can do it:
I'm not sure I'm following the second (updated) question you have. Once the object is parsed back into an object, why can't you just initialize the next and final properties to valid objects before calling any of the object's methods? You can even add tests into that method that checks for the existence of final and next before returning anything.
var myObj = {
next: null,
final:[],
delimiter: '~',
header: false,
step: function (row) {
var item = {
'line_code': row.data[0][0],
'order': row.data[0][1]
};
args.config.config.final.push(item);
},
complete: function (result) {
console.log('Reading data completed. Processing.');
return args.config.config.next(null, args.config.config.final);
},
error: function () {
console.log('There was an error parsing');
}
};
// Stringify the object using a replacer function that will explicitly
// turn functions into strings
var myObjString = JSON.stringify(myObj, function(key, val) {
return (typeof val === 'function') ? '' + val : val;
});
// Now, parse back into an object with a reviver function to
// test for function values and create new functions from them:
var obj = JSON.parse(myObjString, function(key, val){
// Make sure the current value is not null (is a string)
// and that the first characters are "function"
if(typeof val === "string" && val.indexOf('function') === 0){
// Isolate the argument names list
var start = val.indexOf("(") + 1;
var end = val.indexOf(")");
var argListString = val.substring(start,end).split(",");
// Isolate the body of the function
var body = val.substr(val.indexOf("{"), val.length - end + 1);
// Construct a new function using the argument names and body
// stored in the string:
return new Function(argListString, body);
} else {
// Non-function property, just return the value
return val;
}
}
);
// Test the method:
obj.error(); // 'There was an error parsing' is written to console.
// Examine the object:
console.log(obj);

Related

Understand JSON result

In my code I have to analyse JSON objects. I use a small function set:
visit = function(object) {
if (isIterable(object)) {
forEachIn(object, function (accessor, child) {
visit(child);
});
}
else {
var value = object;
console.log(value);
}
};
forEachIn = function(iterable, functionRef) {
for (var accessor in iterable) {
functionRef(accessor, iterable[accessor]);
}
};
isIterable = function(element) {
return isArray(element) || isObject(element);
};
isArray = function(element) {
return element.constructor == Array;
};
isObject = function(element) {
return element.constructor == Object;
};
If I throw now a JSON Object to the visit function, it give me just the value to the console. But I expected the key/value combination. Example:
Code throw
aa03ddbffe59448fb8a56f6b80e650053
But I expect
uuid: aa03ddbffe59448fb8a56f6b80e650053
Is there anything I misunderstand?
I think the value variable must contain a different type from what you're expecting. You could try putting a breakpoint on that line of code and inspecting the object to check what it is. You're expecting the value variable to contain an object with a single uuid property, but it looks to me like the variable actually just contains a string.

Pass a javascript function as part of a JSON [duplicate]

I've been looking around for a way to do this but can't seem to find anything, I have different configuration objects that I need to save as a text in variables for some processing later on, here is a sample:
object:
args.config.config = {
next: null,
final:[],
delimiter: '~', header: false,
step: function (row) {
var item = {
'line_code': row.data[0][0],
'order': row.data[0][1]
}
args.config.config.final.push(item);
},
complete: function (result) {
console.log('Reading data completed. Processing.');
return args.config.config.next(null, args.config.config.final);
},
error: function () {
console.log('There was an error parsing');
}'
}
I need to save this as a string, so something like:
args.config.config = "{object goes here}";
Without putting everything on one giant line or adding break line characters as this will be parsed later to be used in a config, and that will mess things up, any ideas?
UPDATE:
So changing them into text may not be the best solution, these configs will be stored in a mongo database, so it may take them as is (I have not tried it yet).
One of the other problems I was running into was that in the config object I had this:
final.push(item)
and
return next(null, final)
Which will be defined in another file using the config object:
other file:
exports.parse = function(args, next){//next is what I need to call in the config
var final = []; //this is the final referred to in the config object
....
Baby.parse(data, args.config)
}
So the return next(null, final) and final.push(result) have to refer the the var / function in the new file, but I have no idea how to get that to work, that't why I had to add a final array in the config object and a null next function, then assign it like so:
exports.parse = function(args, next){
args.config.next = next;
....
Baby.parse(data, args.config)
}
the object was calling it with the ugly line:
return args.config.config.next(null, args.config.config.final);
If anyone has a way around this, it would be much appreciated.
If you use JSON.stringify with a "replacer" function and
JSON.parse with a "reviver" function along with new Function(), you can do it:
I'm not sure I'm following the second (updated) question you have. Once the object is parsed back into an object, why can't you just initialize the next and final properties to valid objects before calling any of the object's methods? You can even add tests into that method that checks for the existence of final and next before returning anything.
var myObj = {
next: null,
final:[],
delimiter: '~',
header: false,
step: function (row) {
var item = {
'line_code': row.data[0][0],
'order': row.data[0][1]
};
args.config.config.final.push(item);
},
complete: function (result) {
console.log('Reading data completed. Processing.');
return args.config.config.next(null, args.config.config.final);
},
error: function () {
console.log('There was an error parsing');
}
};
// Stringify the object using a replacer function that will explicitly
// turn functions into strings
var myObjString = JSON.stringify(myObj, function(key, val) {
return (typeof val === 'function') ? '' + val : val;
});
// Now, parse back into an object with a reviver function to
// test for function values and create new functions from them:
var obj = JSON.parse(myObjString, function(key, val){
// Make sure the current value is not null (is a string)
// and that the first characters are "function"
if(typeof val === "string" && val.indexOf('function') === 0){
// Isolate the argument names list
var start = val.indexOf("(") + 1;
var end = val.indexOf(")");
var argListString = val.substring(start,end).split(",");
// Isolate the body of the function
var body = val.substr(val.indexOf("{"), val.length - end + 1);
// Construct a new function using the argument names and body
// stored in the string:
return new Function(argListString, body);
} else {
// Non-function property, just return the value
return val;
}
}
);
// Test the method:
obj.error(); // 'There was an error parsing' is written to console.
// Examine the object:
console.log(obj);

Why is the finallyDecrypt method in this code undefined?

My app receives a base64 encoded value that is also encrypted. The data can come in a few different ways so I wanted to create chain-able methods to keep the code clean and modular.
I want to be able write: decryptionChain.decodeBase64(b64Value).stringToBuffer().finallyDecrypt();
When I run the code, the last property method "finallyDecrypt" returns as undefined.
Why is the "finallyDecrypt" method coming back as undefined? The rest all works and if I run encryptionChain.decodeBase64(b64Value).stringToBuffer() I get back the Buffer I expect. It is only when the finallyDecrypt is chained in that I error out.
Here is the code:
function decrypt(encrypted) {
var decipher = crypto.createDecipheriv(algorithm, password, iv);
decipher.setAuthTag(encrypted.tag);
var dec = decipher.update(encrypted.content, 'hex', 'utf8');
dec += decipher.final('utf8');
return dec;
}
var decryptionChain = {
currentValue:"",
decodeBase64: function (encryptedValue){
this.currentValue = new Buffer(encryptedValue.toString(), "base64");
return this;
},
stringToBuffer: function() {
if (this.currentValue) {
myBuffer = JSON.parse(this.currentValue, function (key, value) {
return value && value.type === 'Buffer'
? new Buffer(value.data)
: value;
});
}
return myBuffer;
},
finallyDecrypt : function(myBuffer){
if(myBuffer){
decrypt(myBuffer);
}
return this;
}
};
Chaining works by returning this from each method (which points back to the decryptionChain object).
stringToBuffer, however, returns myBuffer, so you're trying to call a method named finallyDecrypt on that buffer (which doesn't have that method, hence the error).
If you want it to work with chaining, use something similar to how you're dealing with currentValue:
stringToBuffer : function() {
...
this.myBuffer = myBuffer;
return this;
},
finallyDecrypt : function() {
if (this.myBuffer) {
...
}
}
The stringToBuffer() function returns a Buffer (or it may return nothing if this.currentValue is falsey). The function you're trying to call is not in Buffer, it's in decryptionChain, so when you try to call a non-existing function on Buffer, it's telling you it's undefined.
Notice in decodeBase64(), you store this.currentValue and then return this. That's why the next chain works, because the decryptionChain object is being returned.
So stringToBuffer() could store it's result in this.bufferedResult, and then stringToBuffer() could return this.

What is the most efficient way for checking if an object parameter has all require properties?

In javascript using an object parameter is my preferred way of working with functions. To check that a function has the required parameters I either (Solution 1) loop through all the object parameters properties and throw an error or (Solution 2) wait until a required property is needed and throw an error. Solution two seems efficient but I have to throws in multiple places in the function. Solution 1 seems pragmatic but should probably be a reusable piece of code. Is there another solution I should be looking at?
You can actually do this
var propsNeeded = ["prop1", "prop2", "blah", "blah", "blah"],
obj = {
prop1: "Hi"
}
function hasRequiredProperties(props, obj){
return Object.keys(obj).sort().join() == propsNeeded.sort().join();
}
console.log(hasRequiredProperties(propsNeeded, obj)); // false
You can check for single properties like
function hasProperty(propName, obj){
return obj.hasOwnProperty(propName);
}
For consistency I would create require method and use it always when some property is required.
var require = function (key, object) {
if (typeof object[key] === 'undefined') {
throw new Error('Required property ' + key + ' is undefined');
}
};
I would test if required property exists as soon as I'm certain that property is needed. Like this:
var example = function (args) {
require('alwaysRequired', args);
// some code here which uses property alwaysRequired
if (args.something) {
require('sometimesRequired', args);
// some code here which uses property sometimesRequired
}
};
Using #Amit's answer I'd probably add a method to Object itself:
Object.prototype.hasAllProperties = function(props, fire){
var result = Object.keys(this).sort().join() == propsNeeded.sort().join();
if (fire && !result){
throw new Error('Object does not define all properties');
}
return result;
}
and in your function:
function someFunction(myObject){
var objComplete = myObject.hasAllProperties(["prop1", "prop2", "prop3"], false);
}
Update:
After noticing the problem with #Amit's original answer, here's what I suggest:
Object.prototype.hasAllProperties = function(props, fire){
var result = true;
$(props).each(function(i, e){
if (!this.hasOwnProperty(e) ) {
result = false;
return false;
}
});
if (fire && !result){
throw new Error('Object does not define all properties');
}
return result;
}
This is just a general case of checking for presence of keys on a object, which can be done easily enough with
requiredParams.every(function(prop) { return prop in paramObj; })
It almost reads like natural language. "Taking the required parameters, is EVERY one of them IN the parameter object?".
Just wrap this in function checkParams(paramObj, requiredParams) for easy re-use.
More generally, this is the problem of asking if one list (in this case the list of required parameters) is included in another list (the keys on the params object). So we can write a general routine for list inclusion:
function listIncluded(list1, list2) {
return list1.every(function(e) { return list2.indexOf(e) !== -1; });
}
Then our parameter-checking becomes
function checkParams(paramObj, requiredParams) {
return listIncluded(requiredParams, Object.keys(paramObj));
}
If you want to know if object has at least some properties you can use this function without third parameter:
function hasRequiredProperties(propsNeeded, obj, strict) {
if (strict) return Object.keys(obj).sort().join() == propsNeeded.sort().join();
for (var i in propsNeeded ) {
if (!obj.hasOwnProperty(propsNeeded[i])) return false;
}
return true;
};
Example:
options = {url: {
protocol: 'https:',
hostname: 'encrypted.google.com',
port: '80'
}
};
propsNeeded = ['protocol', 'hostname'];
hasRequiredProperties(propsNeeded, options.url); // true
hasRequiredProperties(propsNeeded, options.url, true); // false

get value of function in json string

I'm tring to get some json to contain a function so that the option in the plugin gets the returned value of the function that was in the json string.
The set up is as such:
default.txt
[
{
"element":"a[href$='.jpg']",
"options":{
"label":"test"
}
},{
"element":"a#hover",
"options":{
"label":(function(ele){ return 'test'; })()
}
}
]
and the plug-in is
(function($) {
function defined(obj){return typeof(obj)!=='undefined';}
function evaluate(ele, obj) {
if(typeof obj === 'function') {
//alert('is function');
obj = obj(ele);
}else{
try{
//alert('thinking it may be a function still');
obj = eval("("+obj+"("+ele+"));");
//obj = (obj)(ele);
}catch(err){
//Handle errors here
//alert('not function');
}
}
return obj;
}
function debug(message) {
console.debug(message);
}
$.set = function(options){
var settings = $.extend({}, {}, options);
if(defined(settings.actions)){
$.each(settings.actions, function(index, value) {
$(value.element).do_action(defined(value.options)?value.options:null);
});
}
}
$.fn.do_action = function(options) {
// Add event handler to all matching elements
return this.each(function() {
var ele = $(this);
var settings = $.extend({}, $.fn.do_action.defaults, options);
var label= evaluate(ele, settings.label);
var message = "label:'" + label + "'";
debug('Tracking ' + action + ' ' + message);
});
};
$.fn.do_action.defaults = {
label : function(ele) { return ele.attr('href'); }
};
}(jQuery));
and the control is
$.getJSON('default.txt' , function(data){
$.set({
actions:data // this si where we pull in the options
});
});
I seems to work if I put in an alert inside the anonymous function, so from
"label":(function(ele){ return 'test'; })()
to
"label":(function(ele){ alert('test') })()
but after that I can't seem to get it to print right in the console as I only get the message
Tracking label:'(function(ele){ return 'test'; })
[EDIT]
Please stop saying that "you should not put code in json". If you work for Google and are their top level programmers getting paid the big super bucks then I'll listen to the why's, but I'm sorry if it's good for them, it'll work for me. Don't know what else to say, I already understand the point of why you should avoid it which I aim to do as well. There are times when you just can't and why i supect Google does the same.
Your label member is not a function; it's a value returned by a function. The final () at the end means "execute this function immediate whenever the value of label is requested and provide its return value as the value of label".
That said, if you must include functions in your JSON object (and from what you've said, you do) your best bet is to stringify-and-eval your functions. You should also be able to use raw functions in your JSON, like {foo : (function(){ ... }) }, though I've personally had a tricky time getting this right (but that's just me -- if you can get it right, go for it).
(As an aside, it's not possible to stringify functions from existing objects, but if you're getting your functions as text from user/programmer input, that's not an issue.)

Categories