29

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?

markusf
  • 303
  • 1
  • 3
  • 5

11 Answers11

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/

itsatony
  • 987
  • 1
  • 9
  • 13
  • 1
    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](http://jsfiddle.net/VUZck/1/) – cboler Aug 23 '12 at 14:22
  • 3
    @RequiredCheese It does work when iterating over objects, you were only iterating the array you defined, not the members of the object. Updated fiddle: http://jsfiddle.net/VUZck/17/ – dmck Feb 19 '13 at 17:21
26

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

var f = function(a, b){
    return a + b; 
}
var str = String(f);
Milk3dfx
  • 617
  • 1
  • 6
  • 10
14

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 **/
})
SIMDD
  • 615
  • 5
  • 15
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).

maerics
  • 151,642
  • 46
  • 269
  • 291
  • 3
    As a matter of fact, `Function.prototype.toString()` is defined by the specification. – user123444555621 Mar 11 '11 at 01:39
  • 2
    @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. – maerics Mar 11 '11 at 08:18
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.

Michael Wallace
  • 402
  • 3
  • 9
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

CarbonMan
  • 4,350
  • 12
  • 54
  • 75
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));
Ori
  • 367
  • 3
  • 16
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
};
Steven Kaspar
  • 1,147
  • 10
  • 14
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/

Shobhit Sharma
  • 604
  • 1
  • 9
  • 18
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
}
Alex Sorkin
  • 101
  • 3
-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
    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. – Michał Tkaczyk Nov 03 '20 at 10:13