29

I'm looking for a solution to serialize (and unserialize) Javascript objects to a string across browsers, including members of the object that happen to be functions. A typical object will look like this:

{
   color: 'red',
   doSomething: function (arg) {
        alert('Do someting called with ' + arg);
   }
}

doSomething() will only contain local variables (no need to also serialize the calling context!).

JSON.stringify() will ignore the 'doSomething' member because it's a function. I known the toSource() method will do what I want but it's FF specific.

5
  • 1
    I'll just leave a comment because I'm not offering a full solution. One thing that may help is that you can stringify a Function with a call to toString(). jsfiddle.net/HFMaz Not sure about cross-browser support. Commented Sep 10, 2010 at 15:14
  • 1
    this is asked regularly but i can't find anything relevant :\ why are do you need to serialize the function anyway? perhaps there's a better way to arrange your code? Commented Sep 10, 2010 at 15:20
  • This is for WYSIWYG project. Each Javascript object (with methods) defines the behavior of page components. The page contents (including JS behavior) has to be saved server-side, hence serialized. Commented Sep 10, 2010 at 15:43
  • how is the code created? it seems like you should have access to a string representation of all this at some earlier point in the process. Commented Sep 10, 2010 at 15:52
  • I hope that this isn't used by the wide public then... otherwise you'll have to make sure that there's no way I can XSS that thing. Commented Sep 10, 2010 at 17:28

6 Answers 6

15

You can use JSON.stringify with a replacer like:

JSON.stringify({
   color: 'red',
   doSomething: function (arg) {
        alert('Do someting called with ' + arg);
   }
}, function(key, val) {
        return (typeof val === 'function') ? '' + val : val;
});
Sign up to request clarification or add additional context in comments.

1 Comment

I ended up using JSON.stringify(myObject, function(key, val) { return (typeof val === 'function') ? '[function]' : val; }, 4); and it outputs really nice.
9

A quick and dirty way would be like this:

Object.prototype.toJSON = function() {
  var sobj = {}, i;
  for (i in this) 
    if (this.hasOwnProperty(i))
      sobj[i] = typeof this[i] == 'function' ?
        this[i].toString() : this[i];

 return sobj;

};

Obviously this will affect the serialization of every object in your code, and could trip up niave code using unfiltered for in loops. The "proper" way would be to write a recursive function that would add the toJSON function on all the descendent members of any given object, dealing with circular references and such. However, assuming single threaded Javascript (no Web Workers), this method should work and not produce any unintended side effects.

A similar function must be added to Array's prototype to override Object's by returning an array and not an object. Another option would be attaching a single one and let it selectively return an array or an object depending on the objects' own nature but it would probably be slower.

function JSONstringifyWithFuncs(obj) {
  Object.prototype.toJSON = function() {
    var sobj = {}, i;
    for (i in this) 
      if (this.hasOwnProperty(i))
        sobj[i] = typeof this[i] == 'function' ?
          this[i].toString() : this[i];

    return sobj;
  };
  Array.prototype.toJSON = function() {
      var sarr = [], i;
      for (i = 0 ; i < this.length; i++) 
          sarr.push(typeof this[i] == 'function' ? this[i].toString() : this[i]);

      return sarr;
  };

  var str = JSON.stringify(obj);

  delete Object.prototype.toJSON;
  delete Array.prototype.toJSON;

  return str;
}

http://jsbin.com/yerumateno/2/edit

4 Comments

Great, except arrays are not serialized as arrays anymore.
What would the parse function look like? All of the functions are in strings so on parse we need to convert them back to functions.
Hi MooGoo, I'm stuck in with this after stringifying, how do we parse to get back the original function?
Hi Justin, did you have a solution to parse back to the original function?
1

Something like this...

(function(o) {
    var s = "";
    for (var x in o) {
        s += x + ": " + o[x] + "\n";
    }
    return s;
})(obj)

Note: this is an expression. It returns a string representation of the object that is passed in as an argument (in my example, I'm passing the in an variable named obj).

You can also override the toString method of the Object's prototype:

Object.prototype.toString = function() {
    // define what string you want to return when toString is called on objects
}

Comments

1

It's impossible without the help of the object itself. For example, how would you serialize the result of this expression?

(function () {
  var x; 
  return {
      get: function () { return x; },
      set: function (y) { return x = y; }
    };
})();

If you just take the text of the function, then when you deserialize, x will refer to the global variable, not one in a closure. Also, if your function closes over browser state (like a reference to a div), you'd have to think about what you want that to mean.

You can, of course, write your own methods specific to individual objects that encode what semantics you want references to other objects to have.

Comments

1

JSONfn plugin is exactly what you're looking for.

http://www.eslinstructor.net/jsonfn/

--Vadim

1 Comment

I have tested with my object that has function but it isn't working.
1

Perfectly stringify functions in objects by a replacer-argument in JSON.stringify(). The bad thing is that it puts apostrophes around the returned value. The apostrophes is impossible to know where to remove when the value is a function; like {"doSomething": "function(arg){...}"}. Instead return a PLACEHOLDER that is later replaced by the value.

var myObj = {
  color: 'red',
  doSomething: function (arg) {
  alert('Do someting called with ' + arg);
  }
}

var placeholder = '____PLACEHOLDER____';
var fns = [];
var json = JSON.stringify(myObj, function(key, value) {
  if (typeof value === 'function') {
    fns.push(value);
    return placeholder;
  }
  return value;
}, 2);

json = json.replace(new RegExp('"' + placeholder + '"', 'g'), function(_) {
  return fns.shift();
});

console.log(json)

https://gist.github.com/cowboy/3749767

Comments

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.