4

I need to get the result of lazily evaluated getters into JSON. I'm not interested in preserving the method calls themselves.

I'm working on a project where we have an extensive trees of ES6 objects and have been using the Mustache library to turn these trees into an HTML representation. Throughout the project, when we've needed a simple calculated field, we've used getters.

class Foo {
   get bar() {
      return 1 + 2;
   }
}

Mustache can reference these as if they were properties, and all is good with the world.

Now we're moving to a hosted solution for our templates. However, these object trees are being sent through JSON.stringify to send the data to the hosted service. In doing so, none of these getters are showing up.

f = new Foo();
JSON.stringify(f);  // == '{}', hoping for '{"bar":3}'

If I were to build these objects with the old way of prototypical inheritance using __defineGetter__ it works as I need it to. But that would require me to rebuild all the objects and not use the nice ES6 classes.

f = {};
f.__defineGetter__('bar', function() { return 1 + 2;} );
JSON.stringify(f);  // == '{"bar":3}'  Woot!

And so I've been trying different methods of rendering these within the class - the ugly brute force way is to rewrite them all as object properties and re-update them as the contents change. Is there a more elegant way?

I was considering adding a toJSON method to these objects that would generate the appropriate data, but I can't seem to dynamically retrieve a list of getters.

class Foo {
  get bar() {
    return 1 + 2;
  }

  toJSON() {
    return JSON.stringify(Object.assign({}, this));
  }
}

However the Object.assign ignores the getter as well and doesn't solve the issue. I'd have to resort to getting and storing each value.

toJSON() {
  let result = Object.assign({}, this);
  result.bar = this.bar;
  return JSON.stringify(result);
}

But now I'll have to maintain a separate list of these getters, which will be error prone. (As devs add a getter they'll have to remember to add it to the list or it will mysteriously not show in the template.)

To be clear, I am not interested in serializing the methods, I need the lazily calculated result to show up in the JSON, exactly as it does with the deprecated __defineGetter__ and exactly the way Mustache was handling it.

Any ideas?

Ian E
  • 304
  • 1
  • 7
  • 1
    Methods/getters are not part of JSON and thus will not show up. You will can invent your own string format for them, make that data be a string property before you `JSON.stringify()` and then after `JSON.parse()`, you will need a post processing step to reattach the methods/getters from the string properties you previously put on them. JSON was designed to transfer data only, not classes. Usually, what one would do is to have the class definition on both ends and create an instance of the class and then feed it the JSON to initialize itself. Methods/getters from local class definition. – jfriend00 Oct 25 '16 at 21:23
  • @jfriend00 I'm aware that getters and setters are not part of JSON and I don't want them treated any differently than any other value, just calculated lazily. Note that if the getter is assigned without a class (the old prototypical way using `__defineGetter__`) it WILL be included with `JSON.stringify`: f = {} f.__defineGetter__('bar', function() { return 1 + 2; } ); JSON.stringify(f); // == '{"bar":3}' – Ian E Oct 25 '16 at 22:09
  • I've updated the question to indicate what I mean about prototypical inheritance. – Ian E Oct 25 '16 at 22:15
  • JSON is not JavaScript. JavaScript functions are not valid JSON. – Jared Smith Oct 25 '16 at 22:24
  • 1
    You can _totally_ do this. Check out this gist: https://gist.github.com/GMali/8407793edd602474cca8f71ba6099337 – Gbert90 Oct 25 '16 at 23:14

0 Answers0