Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JS : how to convert a string to JS Object (**NOT** to JSON)?

I know how to parse a valid JSON string : JSON.parse('{"key" : "value"}').

But what about a valid JS object, but invalid JSON, like : JSON.parse("{ key : 'value'}") ? The example above throws :

Uncaught SyntaxError: Unexpected token k in JSON at position 2

My actual goal is even trickier. I want to parse a string of a JS object containing RegEx (unsupported by JSON but supported by JS) into a JS object :

'{ key1 : /val1/g , key2 : /val2/i }'

I eventually want to use this object with Mongoose and find documents with it :

Model.find({
     key1 : /val1/g ,
     key2 : /val2/i
})

I have tried applying a fairly complex RegEx to my String, replacing /val1/g with new RegEx("val1","i") :

str = str.replace( /\/(.+?)\/(g?i?).+?(?=,|})/g , "new RegExp(`$1`,`$2`)" )

The .replace() operation works and modifies the string the way I want it. It yields :

{ key1 : new RegExp("val1","g") , key2 : new RegExp("val2","i") }

But when I try to apply JSON.parse to it, it still fails because new RegEx("val1","i")is not a valid value.

like image 252
Jeremy Thille Avatar asked Dec 18 '22 14:12

Jeremy Thille


2 Answers

If you control and can trust the text you're converting, you can use eval:

var str = '{ key1 : /val1/g , key2 : /val2/i }';
var obj = eval("(" + str + ")");
console.log(obj.key1);

Note that when doing the eval, since your expression starts with {, you have to wrap it in () so the parser knows that's starting an object initializer, not a block.

A couple of notes about eval:

  1. It allows arbitrary code execution. So you really have to trust the text you're evaling. Don't eval user input.

  2. Code in an execution context directly containing a call to eval basically cannot be optimized, because the JavaScript engine can't know, when parsing the code, what the string will contain. So while I suppose technically it's premature optimization, I'd tuck the above off in a function that you call from your main logic rather than embedding it in your main logic directly.

like image 138
T.J. Crowder Avatar answered Jan 15 '23 17:01

T.J. Crowder


I faced simmilar issue and solved it with the help of split and reduce.

const getObjectFromString = (stringToParse) => {
    if(typeof stringToParse === 'string'){
        let currentKey = '';
        const keyValPairArr = stringToParse.replace('{','').replace('}','').split(':');
        return keyValPairArr.reduce((obj,current,index,arr)=>{
            const previousKey = currentKey;
            const arrKeyVal = current.trim().split(',');
            currentKey = index!==arr.length-1?arrKeyVal.pop().trim():'';
            const previousVal = arrKeyVal.join(',');
            if(previousKey&&previousVal!=='')obj[previousKey]=previousVal;
            return obj;
        },{})
    }else{
        return stringToParse||{};
    }
}

// following are some results

const example1 = getObjectFromString('{first : 1, second : 2nd, third: "third, 3rd" }')
console.log(example1) // output: {first: '1', second: '2nd', third: '"third, 3rd"'}

It returns either empty object or the converted object from the passed string.

like image 37
Abhishek Singh Avatar answered Jan 15 '23 17:01

Abhishek Singh