5

In my game, I save the current state by converting all the objects to JSON and then saving that to a file. Some objects, like enemies, have functions on them, but JSON can't save functions! Is there an alternative or a solution?

corazza
  • 31,222
  • 37
  • 115
  • 186
  • 4
    Because json deserializes _data_. – Raynos Oct 31 '11 at 15:41
  • Are your functions instance specific or "class" specific? – Vala Oct 31 '11 at 15:42
  • What would happen with the scope of a function when it it serialized and deserialized? A function is more than just its code as string representation. – pimvdb Oct 31 '11 at 15:45
  • 3
    @sissonb fail. `.data` is completely irrelevant / useless. – Raynos Oct 31 '11 at 15:51
  • Didn't see you were saving to file. The .data method does save object's functions though, just not to file. – sissonb Oct 31 '11 at 16:03
  • 1
    Why would you want to serialise the functions? Isn't that a security risk (somebody could hack the serialised functions to put in any arbitrary code. – JeremyP Oct 31 '11 at 16:50

2 Answers2

7
var Enemy = {
  toJSON: function () {
    // pack it up
  },
  fromJSON: function (json) {
    // unpack it.
  },
  /* methods */
};

var e = Object.create(Enemy);
var json = JSON.stringify(e);
var same_e = Enemy.fromJSON(json);

the .toJSON method is a standard interface of JSON.stringify it will look this method and call it if it exists, it will stringify the returned object.

The .fromJSON method is just a named constructor for your Enemy object.

Concrete example JSfiddle

var Enemy = {
  constructor: function(name, health) {
    this.health = health || 100;
    this.name = name;
  },
  shootThing: function (thing) { },
  move: function (x,y) { },
  hideBehindCover: function () {},
  toJSON: function () { 
    return {
      name: this.name,
      health: this.health
    };
  },
  fromJSON: function (json) {
    var data = JSON.parse(json);
    var e = Object.create(Enemy);
    e.health = data.health;
    e.name = data.name;
    return e;
  }
}

var e = Object.create(Enemy);
e.constructor("bob");
var json = JSON.stringify(e);
var e2 = Enemy.fromJSON(json);
console.log(e.name === e2.name);

Meta-option:

A meta option would be to write the class name to the object

Game.Enemy = {
  ...
  class: "Enemy"
};

Then when you load all your json data you just do

var instance = Game[json.class].fromJSON(json);

Community
  • 1
  • 1
Raynos
  • 166,823
  • 56
  • 351
  • 396
  • So basically I have to make new instances of enemies each time the game starts? Oh well, a little bit more work, but no matter... – corazza Oct 31 '11 at 15:45
  • @bane new instances is cheap. You can meta-ify this if you wanted. – Raynos Oct 31 '11 at 15:48
  • @Raynos This won't work if the enemies are deep in the object tree, right? – thejh Oct 31 '11 at 15:48
  • @thejh what do you mean? If the JSON object represents the game containing an array of enemies? Yes then you would have a Game.fromJSON method that unpacked that json object completely – Raynos Oct 31 '11 at 15:50
  • Could you provide a reference link, that this is standard, please? –  Aug 29 '13 at 17:37
1

I think that you have to save the type on your object so that the functions can be re-added at parsing time. E.g. put a type property on your enemy in the constructor. At parsing time, first parse the string like normal JSON and then deep-traverse the resulting object. When you encounter something that was an Enemy, re-attach the methods or so.

thejh
  • 44,854
  • 16
  • 96
  • 107