如何序列化嵌套 Backbone 的集合和模型?

How to serialize nested Backbone's collections and models?

在我的项目中,我使用Backbone集合来整理应用程序的数据,我还需要将我的数据同步到本地存储。我的数据是一个两层深度嵌套的集合和模型,这就是问题所在。

内部集合同步到localstorage后,变成原始对象数组。所以采集方式(如add)无法使用

经过调试和谷歌搜索,我找到了原因:

当 localStorage 序列化模型时,它调用 model.toJSON(),它只是克隆模型的属性,不包括嵌套集合。

// Return a copy of the model's `attributes` object.
toJSON: function(options) {
  return _.clone(this.attributes);
},

所以它使用 Underscore's clone function,文档说它:

Create a shallow-copied clone of the provided plain object. Any nested objects or arrays will be copied by reference, not duplicated.

所以我正在寻找一种深层复制方法来覆盖默认模型的 .toJSON。但我想不出正确的方法。

例如,我尝试了以下操作:

Backbone.Model.prototype.toJSON = function() {
    var json = $.extend(true, {}, this.attributes);
    return json;
};

编辑,根据Emile的建议,我的真实模型如下:

app.RecordItem = Backbone.Model.extend({

    defaults: {
        target: 1,
        date: '',
        day: '',
        //foodlist: new TrackList(),
        currentvalue: 0,
        isSetup: false,
    },
    initialize: function() {

        var days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
        var months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
        var d = new Date();
        this.set("date", d.getDate() + "." + months[d.getMonth()]);
        this.set("day", days[d.getDay()]);
        //
        var foodlist = this.getFoodlist();
        if (!(foodlist instanceof TrackList)) {
            this.set('foodlist', new TrackList(foodlist));
        }
    },
    getFoodlist: function() {
        if (!this.foodlist) this.foodlist = new TrackList(this.get('foodlist'));
        return this.get('foodlist');
    },

    toJSON: function(options) {
        // this gets the default behavior
        var attrs = this.constructor.__super__.toJSON.apply(this, arguments);
        var foodlist = attrs.foodlist;
        if (foodlist) {
            // then replace the collection reference with the actual serialized data
            attrs.foodlist = foodlist.toJSON(options);
        }
        return attrs;
    },
});

覆盖 toJSON 方法后。错误信息是

"foodlist.toJSON is not a function(…)"

虽然 jQuery's extend 提供深层复制,但这不是您所需要的,原因如下:

localStorage 存储字符串,因此需要序列化为 JSON。函数不会被序列化,因为它们在 JSON1.

中无效

因此,尝试序列化整个 Backbone 集合或模型并不是一个好主意,而是仅序列化数据并在反序列化数据时实例化嵌套结构

Backbone's toJSON

This can be used for persistence, serialization, or for augmentation before being sent to the server. The name of this method is a bit confusing, as it doesn't actually return a JSON string — but I'm afraid that it's the way that the JavaScript API for JSON.stringify works.

默认的 toJSON 行为是对模型的属性进行浅表复制。由于您要嵌套模型和集合,因此需要更改序列化以将嵌套考虑在内。

实现此目的的一个简单方法是覆盖 toJSON 以调用 attributes 散列中每个嵌套集合和模型的 toJSON 函数。

var Daymodel = Backbone.Model.extend({
    defaults: { day: 1, },
    initialize: function(attrs, options) {
        var agenda = this.getAgenda();
        if (!(agenda instanceof Todocollection)) {
            // you probably don't want a 'change' event here, so silent it is.
            return this.set('agenda', new Todocollection(agenda), { silent: true });
        }
    },
    /**
     * Parse can overwrite attributes, so you must ensure it's a collection
     * here as well.
     */
    parse: function(response) {
        if (_.has(response, 'agenda')) {
            response.agenda = new Todocollection(response.agenda);
        }
        return response;
    },
    toJSON: function(options) {
        var attrs = Daymodel.__super__.toJSON.apply(this, arguments),
            agenda = attrs.agenda;
        if (agenda) {
            attrs.agenda = agenda.toJSON(options);
        }
        return attrs;
    },
    getAgenda: function() {
        return this.get('agenda');
    },
    setAgenda: function(models, options) {
        return this.getAgenda().set(models, options);
    },
});

附加信息:


1 虽然将函数序列化为字符串并使用 eval 反序列化并非不可能,但这不是一个好主意,而且完全没有必要这里。