dfahlander / typeson

Preserves types over JSON, BSON or socket.io
MIT License
68 stars 4 forks source link

revive function does not resolve repeated objects #8

Closed mysticflute closed 2 months ago

mysticflute commented 5 years ago

This may be a bug or it may be a doc issue. I'm not exactly sure because the documentation for the revive function does not explain its behavior but just refers to the encapsulate function.

I'm using typeson.encapsulate and typeson.revive as documented in the top usage section.

I have an es6 class A instance with an array of class B instances. class A also has an instance of class C, which is passed the same array of class B. This gives me something like this after calling encapsulate (a little more complicated than I just described because more classes are involved, but same idea):

"sessionContext": {
          "gameContext": {
              "players": [
                  {
                      "name": "Player 1",
                  },
                  {
                      "name": "Player 2",
                  }
              ],
              "_rounds": [
                  {
                      "category": {
                          "_players": "#sessionContext.gameContext.players",
                          "turns": 0,
                      }
                  }
              ]
          }
      },
      "$types": {
          "sessionContext": "SessionContext",
          "sessionContext.gameContext": "GameContext",
          "sessionContext.gameContext.players.0": "Player",
          "sessionContext.gameContext.players.1": "Player",
          "sessionContext.gameContext._rounds.0": "Round",
          "sessionContext.gameContext._rounds.0.category": "Category",
          "sessionContext.gameContext._rounds.0.category._players": "#"
      }
  }

The documentation says this about the cyclic option: "If this property is true, several instances of same object will only occur once in the generated JSON and other references will just contain a pointer to the single reference."

1) the name of this option is slightly misleading, because it kicks in even for references that are not cyclic but simply duplicated.

2) it seems that the default revive function for simple classes does not resolve these pointer references. What I'm seeing is that "_players": "#sessionContext.gameContext.players" is not being assigned to the revived instance but simply dropped.

It's not clear if this is a bug, if it requires some async option, if there is some missing configuration, etc...?

brettz9 commented 5 years ago

I'm not sure why you're seeing a reference getting stringified but not being revived. That sounds like a bug, and it would be great if you could provide a test case for this if this is the case.

However, unless you are dealing with an array or plain object, the default behavior for objects (including ES6 class instances) is to just be passed on, leaving it to JSON.stringify to do any iteration of child objects, unless it is detected as a registered type.

import Typeson from '../typeson.js';

const typeson = new Typeson();

class A {
    constructor (b) {
        this.b = b;
        this.x = new C(b);
    }
}

class C {
    constructor (b) {
        this.b = b;
    }
}

const b = [3, 4, 5];

let a = new A(b);
let tson = typeson.stringify(a, null, 2);
console.log(tson); // Array is duplicated (courtesy of JSON.stringify)

/*
{
  "b": [
    3,
    4,
    5
  ],
  "x": {
    "b": [
      3,
      4,
      5
    ]
  }
}
*/

a = {
    b: b,
    x: {
        b: b
    }
};
tson = typeson.stringify(a, null, 2);
console.log(tson); // Array is encoded as a reference

/*
{
  "b": [
    3,
    4,
    5
  ],
  "x": {
    "b": "#b"
  },
  "$types": {
    "x.b": "#"
  }
}
*/