31

Hey, Im trying to convert specific javascript objects to a String. So far I'm working with json2.js. As soon as my Object contain functions, those functions are stripped. I need a way to convert functions too, any ideas?

There is a toString() method for functions in firefox, but how to make that work with json2.js?

1
  • You could convert the functions to strings beforehand. But why do you want to do this? Commented Mar 10, 2011 at 19:44

11 Answers 11

41

Actually, I think it is possible and easy. At least when doing jsonP with nodeJS it works for me just fine, and it's demonstratable by the following fiddle. I did it by simply adding strings to a function:

var anyString = '';
var aFunction = function() { return true; };
var functionToText = anyString + aFunction;
console.log(functionToText);

here's the fiddle: http://jsfiddle.net/itsatony/VUZck/

Sign up to request clarification or add additional context in comments.

2 Comments

I think OP was talking about having functions as a property of the JavaScript object. Though your solution does work for the simple case of a single function, it does not work (even when iterating over the object) for what the OP was asking. updated fiddle
@RequiredCheese It does work when iterating over objects, you were only iterating the array you defined, not the members of the object. Updated fiddle: jsfiddle.net/VUZck/17
28

Use String() function http://www.w3schools.com/jsref/jsref_string.asp

var f = function(a, b){
    return a + b; 
}
var str = String(f);

1 Comment

Exactly what I needed... I was using toSource() in Firefox but it was failing in Webkit/Blink. Thank you!
17

convert obj to str with below function:

function convert(obj) {
  let ret = "{";

  for (let k in obj) {
    let v = obj[k];

    if (typeof v === "function") {
      v = v.toString();
    } else if (v instanceof Array) {
      v = JSON.stringify(v);
    } else if (typeof v === "object") {
      v = convert(v);
    } else {
      v = `"${v}"`;
    }

    ret += `\n  ${k}: ${v},`;
  }

  ret += "\n}";

  return ret;
}

input

const input = {
  data: {
    a: "@a",
    b: ["a", 2]
  },

  rules: {
    fn1: function() {
      console.log(1);
    }
  }
}

const output = convert(input)

output

`{
  data: {
    a: "@a",
    b: ["a", 2]
  },
  rules: {
    fn1: function() {
      console.log(1);
    }
  }
}`

// typeof is String

convert back

const blob = new Blob([output], { type: 'application/javascript' })
const url = URL.createObjectURL(blob)
import(url).then(input => {
    /** parse input here **/
})

6 Comments

Thanks for this. It was almost like I needed. I had to add another if statement: else if (typeof v === 'boolean') { v = v; }.
Great. Fantastic. I would donate million to you!
how do you convert it back?
Thank you! It is what I need for undocumented object. Excellent if you face lack of documentation in some project. Just to check what function set is available to use instead of references list.
Yes...How do you convert it back?
|
7

The short answer is that you cannot convert arbitrary JavaScript functions to strings. Period.

Some runtimes are kind enough to give you the string serialization of functions you defined but this is not required by the ECMAScript language specification. The "toString()" example you mentioned is a good example of why it cannot be done - that code is built in to the interpreter and in fact may not be implemented in JavaScript (but instead the language in which the runtime is implemented)! There are many other functions that may have the same constraints (e.g. constructors, built-ins, etc).

2 Comments

As a matter of fact, Function.prototype.toString() is defined by the specification.
@Pumbaa: Sure, the toString() method is defined for every object, but there's no requirement to have it serialize the method body or instructions, which the OP implied.
2

I made a improved version based on the @SIMDD function, to convert all types of objects to string.

Typescript code:

function anyToString(valueToConvert: unknown): string {
    if (valueToConvert === undefined || valueToConvert === null) {
        return valueToConvert === undefined ? "undefined" : "null";
    }
    if (typeof valueToConvert === "string") {
        return `'${valueToConvert}'`;
    }
    if (
        typeof valueToConvert === "number" ||
        typeof valueToConvert === "boolean" ||
        typeof valueToConvert === "function"
    ) {
        return valueToConvert.toString();
    }
    if (valueToConvert instanceof Array) {
        const stringfiedArray = valueToConvert
            .map(property => anyToString(property))
            .join(",");
        return `[${stringfiedArray}]`;
    }
    if (typeof valueToConvert === "object") {
        const stringfiedObject = Object.entries(valueToConvert)
            .map((entry: [string, unknown]) => {
                return `${entry[0]}: ${anyToString(entry[1])}`;
            })
            .join(",");
        return `{${stringfiedObject}}`;
    }
    return JSON.stringify(valueToConvert);
}

Vanilla Javascript code:

function anyToString(valueToConvert) {
    if (valueToConvert === undefined || valueToConvert === null) {
        return valueToConvert === undefined ? "undefined" : "null";
    }
    if (typeof valueToConvert === "string") {
        return `'${valueToConvert}'`;
    }
    if (typeof valueToConvert === "number" ||
        typeof valueToConvert === "boolean" ||
        typeof valueToConvert === "function") {
        return valueToConvert.toString();
    }
    if (valueToConvert instanceof Array) {
        const stringfiedArray = valueToConvert
            .map(property => anyToString(property))
            .join(",");
        return `[${stringfiedArray}]`;
    }
    if (typeof valueToConvert === "object") {
        const stringfiedObject = Object.entries(valueToConvert)
            .map((entry) => {
            return `${entry[0]}: ${anyToString(entry[1])}`;
        })
            .join(",");
        return `{${stringfiedObject}}`;
    }
    return JSON.stringify(valueToConvert);
}

ATENTION!

I am using the function Object.entries(), winch currently is a draft. So if you are not using Babel or typescript to transpile your code, you can replace it with a for loop or the Object.keys() method.

Comments

1

Combining a few options

var aObj = { 
    v: 23,
    a: function() { 
        return true; 
    } 
};
var objStr = '';
for (var member in aObj) {
    objStr += (objStr ? ',\n': '')+
        member + ':' + aObj[member] + '';
}   

console.log('{\n'+
    objStr + '\n}');

JSFiddle

Comments

1

functionName.toString() will return a string of all the function code. I cut is after the name.

var funcString = CurrentButton.clickFunc.toString();
console.log("calling:" + funcString.substr(0, funcString.indexOf(")")-1));

Comments

1

// utility for logging
var log = function(s){
  var d = document.getElementById('log');
  var l = document.createElement('div');
  l.innerHTML = (typeof s === 'object')?JSON.stringify(s):s;
  d.appendChild(l);
}


// wrapper function

var obj = {
  'x-keys': {
    'z': function(e){console.log(e);},
    'a': [function(e){console.log('array',e);},1,2]
  },
  's': 'hey there',
  'n': 100
};
log(obj);

// convert the object to a string 
function otos(obj){
  var rs = '';
  var not_first = false;
  
  for(var k in obj){
    if(not_first) rs += ',';
    if(typeof obj[k] === 'object'){
      rs +=  '"'+k+'": {'+otos(obj[k])+'}';
    }
    else if(typeof obj[k] === 'string' || typeof obj[k] === 'function'){
      rs += '"'+k+'":"'+obj[k]+'"';
    }
    else if(typeof obj[k] === 'number'){
      rs += '"'+k+'":'+obj[k]+'';
    }
    else {
      // if it gets here then we need to add another else if to handle it
      console.log(typeof obj[k]);
    }
    not_first = true;
  }
  return rs;
}
// convert a string to object
function stoo(str){
  // we doing this recursively so after the first one it will be an object
  try{
    var p_str = JSON.parse('{'+str+'}');
  }catch(e){ var p_str = str;}
  
  var obj = {};
  for(var i in p_str){
    if(typeof p_str[i] === 'string'){
      if(p_str[i].substring(0,8) === 'function'){
        eval('obj[i] = ' + p_str[i] );
      }
      else {
        obj[i] = p_str[i];
      }
    }
    else if(typeof p_str[i] === 'object'){
      obj[i] = stoo(p_str[i]);
    }
  }
  return obj;
}

// convert object to string
var s = otos(obj);
log(s);
// convert string to object
var original_obj = stoo(s);

log(original_obj);
log( original_obj['x-keys'].z('hey') );
log( original_obj['x-keys'].a[0]('hey') );
<div id='log'></div>

I realize this is very old but I have a solution here

https://jsfiddle.net/stevenkaspar/qoghsxhd/2/

May not work for all cases but it is a good start

It will convert this into a string and then back into an object and you can run the functions

var obj = {
  'x-keys': {
    'z': function(e){console.log(e);},
    'a': [function(e){console.log('array',e);},1,2]
  },
  's': 'hey there',
  'n': 100
};

2 Comments

Hi, please add all the solution here on the reply.
Note that this function will recurse endlessly (stack overflow :) if you supply an object with a circular reference.
1

Just provide the object to this function. (Look for nested function) Here:

function reviveJS(obj) {
  return JSON.parse(JSON.stringify(obj, function (k, v) {
    if (typeof v === 'function') {
      return '__fn__' + v;
    }
    return v;
  }), function (k, v) {
    if (typeof v === 'string' && v.indexOf('__fn__') !== -1) {
      return v;
    }
    return v;
  });
}

UPDATE

A slightly decent code than above which I was able to solve is here http://jsfiddle.net/shobhit_sharma/edxwf0at/

Comments

0

I took one of answers above, it worked fine, but didn't inlcude case then array includes function. So i modified it and it works fine for me.. Sharing the code.

function convert(obj,ret="{") {
    function check(v) {
        if(typeof v === "function") v = v.toString()
        else if (typeof v === "object") v = convert(v)
        else if (typeof v == "boolean" || Number.isInteger(v)) v=v
        else v = `"${v}"`
        return v
    }

    if(obj instanceof Array) {
        ret="["
        obj.forEach(v => {
            ret += check(v)+','
        });
        ret += "\n]"
    } else {
        for (let k in obj) {
            let v = obj[k];
            ret += `\n  ${k}: ${check(v)},`;
        }
        ret += "\n}";
    }
    return ret
}

Comments

-1

So I was just testing your script on one of my project, and there's a problem with Object keys that contain special characters (like / or -).

You should consider wrapping theses keys with quotes.

return `"${entry[0]}" : ${anyToString(entry[1])}`;

1 Comment

Is this an answer to question? If you're trying to update one of the answers and you don't have permissions to do so, just place this in the comments section of that answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.