It is clear that when the attribute (for example id) exists/is defined in the script, we should use obj[id]
and not eval('obj.' + id)
. However, I have not yet found a solution for the case where the attribute name is itself a variable and not defined - for example when recieved as a parameter. In my case I have node.js on the server recieving a JSON object requestData being sent from the client via socket.io. The requestData contains a key/value pair: requestData.key is an array of the node names that make up the JSON path to the element, and requestData.val is the value to be stored in that element.
In the server script there is a defined JSON object called root which contains information relevant to the app, for example:
"root": {
"pages": {
"TM1010": {
"appId": "TM1010",
"componentName": "UserTasksPage"
},
"TM1020": {
"appId": "TM1020",
"componentName": "UsersPage"
}
}
}
If the client were to send a request to update an element - for example to change the value of root.pages.TM1010.componentName - this is how it would be sent from the client:
let requestData = {'key': ['pages', 'TM1010', 'componentName'], 'val': newComponentName};
this.socket.emit('changeData', requestData);
And this is how it is recieved and executed on the server:
socket.on('changeData', (requestData) => {
var str = 'root'
var key = requestData.key
var len = key.length;
for (var i = 0; i < len; i++) {
str = str + '["' + key[i] + '"]'
}
str = str + '=requestData.val'
eval(str)
});
So that what is being executed in the end (the value of str) is:
root["pages"]["TM1010"]["componentName"]=requestData.val
How could this be done without eval()
?
As I mentioned, I do not know the names of those elements that will be sent from the client. All I have on the server is one JSON object called 'root' and I want to continuously add/update to it as I recieve data from the client. I have tried solutions based on JEXL or mathjs but it doesn't seem to work for this type of case.
You could reduce the keys by taking the object and assign the value with the last key.
const
setValue = (object, keys, value) => {
var path = keys.slice(),
last = path.pop();
path.reduce((o, k) => o[k], object)[last] = value;
};
var data = { root: { pages: { TM1010: { appId: "TM1010", componentName: "UserTasksPage" }, TM1020: { appId: "TM1020", componentName: "UsersPage" } } } },
requestData = { key: ['pages', 'TM1010', 'componentName'], val: 'newComponentName' };
setValue(data.root, requestData.key, requestData.val);
console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }