0

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.

knomdlo
  • 398
  • 2
  • 14
  • 1
    `{"name" : this.name, "age": this.age}` is simply not valid JSON. Do you want the service to return invalid JSON? If not, what is the actual data you are working with? – Felix Kling Nov 08 '17 at 06:35
  • 2
    Sounds like you are alowing your users to provide name and age as part of the object they publish, but that you are (rightfully) not trusting them ? Why are the name and the age there in the first place ? JSON is simply a string with format -- you cannot tie server side variables to a json document you receive. – Soren Nov 08 '17 at 06:37
  • This JSON object is actually a config. So we maintain the configs as strings on separate files for easier modifications . Config needs some custom input (like name & age in example ). – knomdlo Nov 08 '17 at 06:56

3 Answers3

0

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);
Titus
  • 22,031
  • 1
  • 23
  • 33
  • This solution helps my case. Can you help me understand why you invoked preProcess() with an empty object through call() rather than directly invoking it. – knomdlo Nov 09 '17 at 12:30
  • @knomdlo That is not required, I've only used it because in a snipped `Object.keys(this)` causes a security exception (trying to access the main `window` object from a frame). Using `call({}....)` sets `this` to a new object rather then the `window` object. – Titus Nov 09 '17 at 14:47
0

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.'
dhilt
  • 18,707
  • 8
  • 70
  • 85
0

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

FrontTheMachine
  • 526
  • 3
  • 14