Binding this to JSON.parse() - javascript

I have a case where I need to parse a string into JS object by binding this like below:
var jsString = '{"name" : this.name, "age": this.age}';
this.name = "Hello";
this.age = 100;
//This fails(CASE 1)
var jsObjectFromString = JSON.parse(jsString);
console.log(jsObjectFromString );
//This works(CASE 2)
var directObject = {"name" : this.name, "age": this.age};
console.log(directObject);
//Need below output for console.log(jsObjectFromString ):
//{"name" : "Hello", "age": 100}
In my actual program, the string is coming from a web-service & hence I can't use CASE 2 approach.
I can traverse the JS object & set params after parsing like below:
var jsonString = '{"name" : "", "age": 0}';
var jsonObject = JSON.parse(jsonString);
jsonObject["name"] = this.name;
jsonObject["age"] = this.age;
But there a lot of inner objects & traversing would be a kill. I tried the below but failed(obviously :( ):
JSON.parse(jsonString).bind(this);
Is there an approach to overcome this?
PS: This is a browser based app not a node project.
Edit: I want to construct a javascript object from the string. I would want to replace parts of the string(like name,age) into actual values in the parsed Javascript object.

This is very strange use case, and I'm sure that the design of your app should be changed. But here is the solution based on the allowed properties white list and an agreement of a specific role of "this." substring in the initial string:
let income = '{"name" : this.name, "age": this.age}';
this.name = "Hello";
this.age = 100;
const whiteList = ['name', 'age'];
const inject = (prop) => {
if(typeof this[prop] === 'string') {
return '"' + this[prop] + '"';
}
return this[prop];
};
whiteList.forEach(prop => income = income.replace('this.' + prop, inject(prop)));
console.log(income); // {"name" : "Hello", "age": 100}
You may upgrade the procedure by
getting the white list from the context automatically
protecting parser by additional symbols ': this.'

as #Soren sayd on the comment, '{"name" : this.name, "age": this.age}' is not a valid JSON string.
What you want to do actually is to pass expressions in the JSON instead of values, and evaluate them. The first thing you could consider is to use eval() that is, in most cases, a bad idea (a nice article here).
another option, maybe more suitable, is to use a custom markup to identify the expressions you want to evaluate, replacing them with your value, and then parsing the obtained string as JSON:
'{"name" : |name|, "age": |age|}' from this string, you can look for any |...| occurrence and use what is within the | symbol (your custom markup marker) as a key inside any object, in your case this[whateverYouFound].
Now you can replace the found value inside the original string to have something like '{"name" : "hello", "age": 100}' (you could even stringify objects, if you need to), that is a valid JSON string.
this method avoid you exposing the possibility to evaluate dangerous code

The best you could do is to generate a valid JSON. Here is an example:
function preProcess(invalidJSON){
this.name = "Hello";
this.age = 100;
Object.keys(this).forEach(key => {
invalidJSON = invalidJSON.replace(new RegExp("this\\." + key, "g"), '"'+this[key]+'"');
});
// replace all non-existing properties with empty values.
var validJSON = invalidJSON.replace(/this\.[^,\[\]\{\}]+/, '""');
return validJSON;
}
var str = '{"name" : this.name, "age": this.age}';
var validJSON = preProcess.call({}, str);
console.log(validJSON);

Related

How to get value of string which has list of arrays from json object

I have this json
var json = {
"I:5.3": {
"conf": {
"name": "Jack"
}
}
}
and this string
var str = '["I.5.3"]["conf"]'
How can get str value from json like
json["I.5.3"]["conf"]
I have json object and str and I have to extract something like
json[str] = json["I.5.3"]["conf"]
Your title suggests you would like to get:
The value of a string.
Which has a list of Array's.
From a JSON object.
However, all your code shows is a Plain JavaScript object. No arrays to be seen... and the only string value I can find is "Jack".
Jack would be extracted like, json["I.5.3"]["conf"]["name"].
Or via dot notation =>
const obj = json["I.5.3"];
const nameValue = obj.conf.name; // => Jack
You have 2 ways(at least that's what's in coming through my mind now):
1st way
var json = {
"I:5.3": {
"conf": {
"name": "Jack"
}
}
}
var str = '["I:5.3"]["conf"]'
var scr_line = 'json'+str;
var target = eval(scr_line);
console.log(target);
2nd way:
var json = {
"I:5.3": {
"conf": {
"name": "Jack"
}
}
}
var str = '["I:5.3"]["conf"]';
let ret = getVal(json, str);
console.log(ret);
function getVal(obj, path){
var regex = /\["(.*?)"\]/mg;
let m;
while ((m = regex.exec(path)) !== null) {
// This is necessary to avoid infinite loops with zero-width matches
if (m.index === regex.lastIndex) {
regex.lastIndex++;
}
if(typeof obj[m[1]] !== 'undefined') obj = obj[m[1]];
else return obj[m[1]];
}
return obj;
}
I would prefer the second because it checks if the property on the object exists
You would have to split the string up into separate parts, I believe.
var str = '["I.5.3"]["conf"]';
var identifiers = str.split('][');
// identifiers would be an array like:
// ["I.5.3", "conf"]
var person = json[identifiers[0]][identifiers[1]];
// person = {
// "name": "Jack"
// }
String.prototype.split() allows you to separate parts of a string out into an array. Checkout the MDN docs to learn more about that particular method.
This specific answer expects that there will always be the properties you split, though and will error out if one is missing. For something a little safer, I would suggest checking to see if that object contains the property you're expecting before trying to access it.
var str = '["I.5.3"]["conf"]';
var identifiers = str.split('][');
try {
var myVal;
// Let's check if the property exists.
if (Object.prototype.hasOwnProperty.call(json, identifiers[0]){
myVal = json[identifiers[0]];
if (Object.prototype.hasOwnProperty.call(myVal, identifiers[1]){
myVal = myVal[identifiers[1]];
}
}
} catch(error) {
// One of the properties didn't exist or something went wrong in the try
// block above.
}
EDIT:
The follow would format your string specifically to meet the match to become an array. If there were single quotes or double quotes anywhere in each array item, this would fall through, so be aware of that.
var myArray = str.slice(2, -2).split('][').map(function(item) {
return item.replace('"', '');
});
String.prototype.slice() extracts a section of a string and returns it as a new string, without modifying the original string.
Then the split method separates it into different array items. Then we iterate over each item in the array and remove additional ". Just to say again, this will fall apart if the original string looks like ['one']['two']. This would also not be reliable if the string looks like ["can't"]["won't"]. So just be aware of that in your particular case. If you're positive that the string will always meet the format you have above, then you can rely on this.

Declare object from string

I want to declare 1 javascript object which its properties are generated from one sequence that was created earlier
example:
var x = '"property1": "abc",
"property2": "def",
"property3": "xyz",
';
var obj = {
// insert content of x here
}
what can i do?
You can declare
var x = {property1: "abc",
property2: "def",
property3: "xyz"};
var obj = {
data:x
}
Assuming you have a string that is a valid sequence of key/value pairs that are double-quoted then you can easily make it into JSON by concatenating '{' and '}', after removing any trailing comma (I notice your last property has a trailing comma). Then you can create an object by parsing that JSON. So:
var x = // Your string of quoted, comma-separated key/value pairs here
var obj = JSON.parse('{' + x.replace(/,\s+$/,'') + '}');
General note of disapproval: I don't know why you would create your properties as a string rather than just adding properties directly to an object, and in general you shouldn't hand-create JSON, and I'd like to note that a string literal enclosed by single-quotes can't have line-breaks in it so I'm hoping you just showed it that way for ease of reading.
Use JSON string here
var x = `{"property1": "abc",
"property2": "def",
"property3": "xyz"
}`;
var obj = JSON.parse(x)
After reading your comments OP I think you may be after bracket notation.
You can access an object's property via dot notation
obj.prop = foo;
var v = obj.prop;
Or you can use bracket notation
var propName = "prop"
obj[propName] = foo;
var v = obj[propName];
Note this is not accessing an array.
So you could do it this way
var data = [["property1", "abc"],["property2","def"],["property3","xyz"]]
var obj = {};
data.forEach(pk => { obj[pk[0]] = pk[1]; });
console.log(obj.property1); // output "abc"
You can use the eval API to parse the string too.
See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval
The eval() function evaluates JavaScript code represented as a string.
Example:
let y = "";
let x = ' "property1": "abc", "property2": "def", "property3": "xyz" ';
eval('y={' + x + '}');
Output:
Please note that using eval can be dangerous as noted in its documentation:
eval() is a dangerous function, which executes the code it's passed
with the privileges of the caller. If you run eval() with a string
that could be affected by a malicious party, you may end up running
malicious code on the user's machine with the permissions of your
webpage / extension.
Example: exposing it as a REST service to allow end-users to hit it with any 'object string' which can instead be a piece of malicious code - like downloading virus.

mongdb/nodejs: using variables in $inc doesn't work

I have the following that i entered into the mongo terminal and it works great
db.cars.update({'_id':'FordXdfg'},{$inc :{'attribs.0.totl':1}})
which basically updates an array using dot notation, the 0 is the index of the array.
this does work. but transferring it to node my 0 comes from a variable.
so i tried
var carIndex = 3;
cars.update({'_id':'FordXdfg'},{$inc :{'attribs.' + carIndex + '.totl':1}}, function (err, callback) ................)
seems to be invalid javascript, if i replace my carIndex with 3 then it works i.e.
cars.update({'_id':'FordXdfg'},{$inc :{'attribs.3.totl':1}}, function (err, callback) ................)
Any ideas?
thanks
When using that style of object initialization in JavaScript, property names must be string literals. When using the object initialization syntax, property names can not be constructed at run time in code. For example, you can only use literals like:
{
"name": "Martin"
"location": "Earth"
"value": 1234
}
You cannot do this:
var propName = "name";
var obj = {
propName: "Martin";
};
While it syntactically appears to work, you'll end up with an object that looks like:
{
propName: "Martin"
}
Again, that's because only literal values are accepted when constructing an object using the shortened syntax. It will not interpret variable values.
There are two other options for setting properties of a JavaScript object, either through simple dot-notation:
obj.name = "Martin";
Or, you can use bracket notation:
obj["name"] = "Martin";
As objects in JavaScript act like associative arrays in that you can define new properties/keys at runtime each with a value, either syntax above works, and both result in the same underlying storage (and can be used interchangeably).
So, you'll need to construct the $inc syntax separately using the other technique for setting object property values in JavaScript:
var inc = {};
inc["attribs." + carIndx + ".totl"] = 1;
Then use that inside of your update:
{ $inc: inc }

Mapping JSON Objects to Javascript Objects

When using AJAX, I tend to pass objects from my server to Javascript in the form of JSON objects (aka Javascript). Certain functions within my Javascript rely on the specific type of object I am using. For instance, lets use a phone number for example. I have a constructor:
function PhoneNumber(number, type, isPrimary, contactId, id, className) {
this.number = number;
this.type = type;
this.isPrimary = isPrimary;
this.contactId = contactId;
this.id = id;
this.className = className;
}
Which I use when creating a phone number object in my Javascript. In some situations I don't create the object in JS, I get the object from the server so it comes in the form of a generic object with the exact same fields. So when my code relies on the specific type by using something such as this:
var objType = objArray[i].constructor.name;
var mappedObj;
switch(objType) {
case 'PhoneNumber':
currentArray = currentArray.phone;
//Convert response to javascript object.
mappedObj = mapPhone(jsonResponse[i]);
break;
case 'Email':
currentArray = currentArray.email;
mappedObj = mapEmail(jsonResponse[i]);
break;
case 'Address':
currentArray = currentArray.address;
mappedObj = mapAddress(jsonResponse[i]);
break;
case 'Website':
currentArray = currentArray.website;
mappedObj = mapWebsite(jsonResponse[i]);
}
In this situation, I check the name of the objects constructor and set certain variables based on that name. If the object I check the name on is a JSON from the server, it simply gives me a generic "Object" response and thus the code does not work. I get around this by using a mapping function for each object such as:
function mapPhone(phoneObj) {
var id = phoneObj.id;
var contactId = phoneObj.contactId;
var number = phoneObj.number;
var type = phoneObj.type;
var primary = phoneObj.isPrimary;
var className = phoneObj.className;
var phoneNumber = new PhoneNumber(number, type, primary, contactId, id, className);
return phoneNumber;
}
This works just fine, but to me seems a little redundant. Is this the best way to solve the JSON Object problem, or is there a better solution? I understand this is more of a "Am I doing this the best way possible" type of question, but I repeat this type of logic CONSTANTLY in my Javascript code and I figure I might as well get another opinion or two on whether or not its the proper way to do this before I have to spend hour upon hour fixing it in the future.
EDIT: I ended up accepting a jQuery solution because I happen to use jQuery in my project. There are however multiple solutions that also worked for me before I found the jQuery option. They just weren't quite as clean and efficient.
The following requires you to have the same properties in your object and your JSON object.
var phoneNumber = $.extend(new PhoneNumber(), yourJSONObject);
This basically creates a new PhoneNumber object and then copies all properties from your JSON object onto it. The $.extend() method is from jQuery, but you could also use as similar method from e.g. Underscore.js or one of the other js libraries/frameworks.
This similar question has a lot of interesting answers:
Parse JSON String into a Particular Object Prototype in JavaScript
Based off the poster's own answer, I think this would be an effective solution for you:
function recastJSON(jsonObject) {
// return generic object if objectType is not specified
if (!jsonObject.objectType)
return jsonObject;
// otherwise create a new object of type specified
var obj = eval('new '+jsonObject.objectType+'()');
for(var i in jsonObject)
obj[i] = jsonObject[i];
return obj;
}
You will need to add objectType to the JSON objects you are receiving to define the javascript class you want to instantiate. Then when you call this function, it will cast the object to that type and copy the data over (including the variable 'objectType').
Using your phone number example, your code would look like this:
// generic object from JSON
var genericObject = {objectType:'PhoneNumber', number:'555-555-5555', type:'home', primary:true, contactId:123, id:1234, className:'stdPhone'};
var phoneObject = recastJSON(genericObject);
AFAIK, in everything that is not IE, you can do this:
// define a class
var Foo = function(name) {
this.name = name;
}
// make a method
Foo.prototype.shout = function() {
return "I am " + this.name;
}
// make a simple object from JSON:
var x = JSON.parse('{ "name": "Jason" }');
// force its class to be Foo
x.__proto__ = Foo.prototype;
// the method works
x.shout();
Unfortunately, IE does not support the __proto__ accessor, so what you would need to do is first create an empty instance of your object, then just copy everything over:
// make a simple object from JSON:
var x = JSON.parse('{ "name": "Jason" }');
// make an empty Foo
var y = Object.create(Foo.prototype);
// copy stuff over
for (a in x) {
y[a] = x[a];
}
y.shout();
Both of these approaches are quite a bit more generic than your mapWhatever functions, keeping it DRY.
If not supporting older browsers is ok, You can use Object.create to do the mapping for you. (dropping the shim—at least the shim at MDN—in will not fix older browsers, since that shim does not accept the second parameter.)
DEMO
function makeThisExtend(obj, CtorFunc) {
for (var k in obj)
if ({}.hasOwnProperty.call(obj, k))
obj[k] = { value: obj[k] };
return Object.create(CtorFunc.prototype, obj);
}
var objFromServer = { Number: "123", id: 5 };
objFromServer = makeThisExtend(objFromServer, PhoneNumber);
alert(objFromServer.Number + " " + objFromServer.id); //123 5
alert(objFromServer.constructor); //function PhoneNumber ...

How to set a Javascript object values dynamically?

It's difficult to explain the case by words, let me give an example:
var myObj = {
'name': 'Umut',
'age' : 34
};
var prop = 'name';
var value = 'Onur';
myObj[name] = value; // This does not work
eval('myObj.' + name) = value; //Bad coding ;)
How can I set a variable property with variable value in a JavaScript object?
myObj[prop] = value;
That should work. You mixed up the name of the variable and its value. But indexing an object with strings to get at its properties works fine in JavaScript.
myObj.name=value
or
myObj['name']=value (Quotes are required)
Both of these are interchangeable.
Edit: I'm guessing you meant myObj[prop] = value, instead of myObj[name] = value. Second syntax works fine: http://jsfiddle.net/waitinforatrain/dNjvb/1/
You can get the property the same way as you set it.
foo = {
bar: "value"
}
You set the value
foo["bar"] = "baz";
To get the value
foo["bar"]
will return "baz".
You could also create something that would be similar to a value object (vo);
SomeModelClassNameVO.js;
function SomeModelClassNameVO(name,id) {
this.name = name;
this.id = id;
}
Than you can just do;
var someModelClassNameVO = new someModelClassNameVO('name',1);
console.log(someModelClassNameVO.name);
simple as this
myObj.name = value;
When you create an object myObj as you have, think of it more like a dictionary. In this case, it has two keys, name, and age.
You can access these dictionaries in two ways:
Like an array (e.g. myObj[name]); or
Like a property (e.g. myObj.name); do note that some properties are reserved, so the first method is preferred.
You should be able to access it as a property without any problems. However, to access it as an array, you'll need to treat the key like a string.
myObj["name"]
Otherwise, javascript will assume that name is a variable, and since you haven't created a variable called name, it won't be able to access the key you're expecting.
You could do the following:
var currentObj = {
name: 'Umut',
age : 34
};
var newValues = {
name: 'Onur',
}
Option 1:
currentObj = Object.assign(currentObj, newValues);
Option 2:
currentObj = {...currentObj, ...newValues};
Option 3:
Object.keys(newValues).forEach(key => {
currentObj[key] = newValues[key];
});

Categories