9

How do I serialize these classes to JSON?

As you can see from the example below JSON.stringify() does not serialize the list of Cache_Backend_LocalStorage_Tag inside the Cache_Backend_LocalStorage_TagThree object.

What am I missing?

interface Cache_Backend_LocalStorage_Tag_Interface {
    _tag : string;
    _keys : string[];
}

class Cache_Backend_LocalStorage_Tag implements Cache_Backend_LocalStorage_Tag_Interface {

    public _tag : string;
    public _keys : string[];

    constructor(tag : string) {
        this._tag = tag;

        this._keys = [];
    }

    public add(key : string) : void {
        this._keys.push(key);   
    }

    public remove(key : string): boolean {
        // Get the index of the key
        var index = this._keys.indexOf(key, 0);

        // Check if we found the keys index
        if (index != undefined) {
           this._keys.splice(index, 1);

           return true;
        }

        return false;
    }

    public get tag(): string {
        return this._tag;
    }

    public get keys(): string[] {
        return this._keys;
    }
}

interface Cache_Backend_LocalStorage_TagThree_Interface {
    _tags : Cache_Backend_LocalStorage_Tag[];
}

class Cache_Backend_LocalStorage_TagThree implements Cache_Backend_LocalStorage_TagThree_Interface {

    public _tags : Cache_Backend_LocalStorage_Tag[];

    constructor(tags : Cache_Backend_LocalStorage_Tag[] = []) {
        this._tags = tags;
    }

    public add(tag : Cache_Backend_LocalStorage_Tag) : void {
        this.tags[tag.tag] = tag;
    }

    public get tags(): Cache_Backend_LocalStorage_Tag[] {
        return this._tags;
    }

    public get(tagKey : string): Cache_Backend_LocalStorage_Tag {
        return this.tags[tagKey];
    }

    public addKeyToTag(tagKey, key) {
        this.tags[tagKey].add(key);
    }

    public removeKeyFromTag(tagKey, key) {
        // Get the tag
        var tag = this._tags[tagKey];

        // Check if we found the tag index
        if (tag != undefined) {
            return tag.remove(key);
        }

        return false;
    }

    public clear(tagKey : string): void {
        delete this._tags[tagKey];
    }

    public static fromObject(object): Cache_Backend_LocalStorage_TagThree {
        return new Cache_Backend_LocalStorage_TagThree(object._tags);
    }
}

Issue:

var tagThree = new Cache_Backend_LocalStorage_TagThree();
tagThree.add(new Cache_Backend_LocalStorage_Tag("stores"));
tagThree.addKeyToTag("stores", "store5");
tagThree.removeKeyFromTag("stores", "store5");

//  {"_tags":[]}
console.log(JSON.stringify(tagThree));

// { _tags: [ stores: { _tag: 'stores', _keys: [Object] } ] }
console.log(tagThree);
David Sherret
  • 101,669
  • 28
  • 188
  • 178
user634545
  • 9,099
  • 5
  • 29
  • 40

1 Answers1

5

Reason

It's because you're assigning properties to an array and array properties won't be included in JSON serialization. For example:

var a = [];
a["test"] = "some value";
JSON.stringify(a); // returns: []

You need to use a plain object:

var o = {};
o["test"] = "some value";
JSON.stringify(o); // returns: {"test":"some value"} 

Solution

Change your Cache_Backend_LocalStorage_TagThree_Interface interface to use a dictionary like object:

interface Cache_Backend_LocalStorage_TagThree_Interface {
    _tags : { [tag: string] : Cache_Backend_LocalStorage_Tag; };
}

Then update all areas of the code that will now have a compile error to use the same type. For example, change:

constructor(tags : Cache_Backend_LocalStorage_Tag[] = []) {

To:

constructor(tags : { [tag: string] : Cache_Backend_LocalStorage_Tag; } = {}) {

Just for fun - Changing default serialization behaviour (Not recommended)

If you really want to make this work with an array with your current setup (I'm not sure why), you can override how serialization is done. To do this, add a toJSON method to the _tags array in Cache_Backend_LocalStorage_TagThree. This allows you to control how the object is serialized when JSON.stringify is called on it. For example:

this._tags.toJSON = function() {
    var values = [];

    for (var v in this) {
        if (this[v] instanceof Cache_Backend_LocalStorage_Tag) {
            values.push(this[v]);
        }
    }

    return JSON.stringify(values);
};
Community
  • 1
  • 1
David Sherret
  • 101,669
  • 28
  • 188
  • 178
  • Thanks for your thorough answer. – user634545 Mar 23 '15 at 16:56
  • @user634545 by the way, you might want to change it to instead use an array. If that's the case, you can still have it as an array, just make sure you store them by a numeric index (ex. `this._tags[0]`) for serialization instead of as property (ex. `this._tags["propertyName"]`). – David Sherret Mar 23 '15 at 22:38
  • Cool, I didn't know about toJson. Thanks – user634545 Apr 26 '15 at 12:56