【问题标题】:JSONAPI serialize nested hasMany relationshipsJSONAPI 序列化嵌套的 hasMany 关系
【发布时间】:2016-01-07 11:43:25
【问题描述】:

我们在此项目中使用 JSONAPI,但由于 [原因] 我们无法在 API 中处理其推荐的关系结构,因此我们将它们作为嵌套对象提供并期望它们改为嵌套对象,格式如下:

{
  "data":{
    "type":"video",
    "id":"55532284a1f9f909b0d11d73",

    "attributes":{
      "title":"Test",

      "transcriptions":{
        "type": "embedded",

        "data":[
          {
            "type":"transcription",
            "id":"203dee25-4431-42d1-a0ba-b26ea6938e75",

            "attributes":{
              "transcriptText":"Some transcription text here. And another sentence after it.",

              "cuepoints":{
                "type":"embedded",

                "data":[
                  {
                    "type":"cuepoint",
                    "id":"bb6b0434-bdc4-43e4-8010-66bdef5c432a",

                    "attributes":{
                      "text":"Some transcription text here."
                    }
                  },
                  {
                    "type":"cuepoint",
                    "id":"b663ee00-0ebc-4cf4-96fc-04d904bc1baf",

                    "attributes":{
                      "text":"And another sentence after it."
                    }
                  }
                ]
              }
            }
          }
        ]
      }
    }
  }
}

我的模型结构如下:

// models/video
export default DS.Model.extend({
  transcriptions: DS.hasMany('transcription')
)};

// models/transcription
export default DS.Model.extend({
  video: DS.belongsTo('video'),
  cuepoints: DS.hasMany('cuepoint')
});

// models/cuepoint
export default DS.Model.extend({
  transcription: DS.belongsTo('transcription')
);

现在,我们要做的是保存video 记录,并让它序列化其中包含的transcriptionscuepoints。我有以下序列化程序,它可以很好地将transcription 嵌入到video 中,即。一个级别,但我需要它然后将cuepoints 也嵌入其中。

export default DS.JSONAPISerializer.extend({
    serializeHasMany: function(record, json, relationship) {
      var hasManyRecords, key;
          key = relationship.key;
          hasManyRecords = Ember.get(record, key);

      if (hasManyRecords) {
        json.attributes[key] = {};

        hasManyRecords.forEach(function(item) {
          json.attributes[key].data = json.attributes[key].data || [];

          json.attributes[key].data.push({
            attributes: item._attributes,
            id: item.get('id'),
            type: item.get('type')
          });
        });
      } else {
        this._super(record, json, relationship);
      }
    }
  });

检查serializeHasMany 方法中的recordjsonrelationship 属性,我看不到任何有关嵌套关系的信息,所以我什至不确定我是否使用了正确的方法。

有什么想法我会出错吗?

【问题讨论】:

  • 您是否研究过 EmbeddedRecordsMixin 的工作原理?可能会为如何继续提供一些灵感。

标签: ember.js ember-data json-api


【解决方案1】:

您必须为每个模型添加序列化程序,并根据需要在正确的序列化程序中调整有效负载。上面的序列化程序会产生您在描述中提供的确切有效负载。

app/serializers/cuepoint.js

import DS from 'ember-data';

export default DS.JSONAPISerializer.extend({

    payloadKeyFromModelName (modelName) {
        return modelName;
    },

    serialize (record, options) {
        return this._super(record, options).data;
    },

    serializeBelongsTo () {}

});

app/serializers/transcription.js

import DS from 'ember-data';

export default DS.JSONAPISerializer.extend(DS.EmbeddedRecordsMixin, {

    attrs: {
        cuepoints: {
            serialize: 'records',
            deserialize: 'records'
        }
    },

    keyForAttribute (key, method) {
        return key;
    },

    payloadKeyFromModelName (modelName) {
        return modelName;
    },

    serialize (record, options) {
        let json = this._super(record, options);
        json.data.attributes.cuepoints = {
            type: 'embedded',
            data: json.data.cuepoints
        }
        delete json.data.cuepoints;
        return json.data;
    },

    serializeBelongsTo () {}

});

app/serializers/video.js

import DS from 'ember-data';

export default DS.JSONAPISerializer.extend(DS.EmbeddedRecordsMixin, {

    attrs: {
        transcriptions: {
            serialize: 'records',
            deserialize: 'records'
        }
    },

    payloadKeyFromModelName (modelName) {
        return modelName;
    },

    serialize (record, options) {
        let json = this._super(record, options);
        json.data.attributes.transcriptions = {
            type: 'embedded',
            data: json.data.transcriptions
        }
        delete json.data.transcriptions;
        return json;
    },

    serializeBelongsTo () {}

});

【讨论】:

  • 谢谢。那么有没有办法以通用的方式做到这一点?我真的希望我能够通过 serializeHasMany 方法中的关系递归循环,但看起来这是不可能的。
  • @MalabarFront 这在语义上是错误的。为什么视频序列化器应该知道如何序列化转录?每个模型都应由其自己的序列化程序进行序列化。这就是 Ember 的工作原理。你不应该尝试破解它(即使通过访问私有属性和方法),因为它可能会在更新过程中导致巨大的问题。
  • 因为我们的 API 和数据结构非常严格,所以所有数据的形状——无论是否嵌套——都是已知的。出于这个原因,我觉得为每个模型编写一个序列化程序是疯狂的。我希望编写一个序列化程序,它知道如何处理嵌套数据,而不管模型被称为什么。
【解决方案2】:

我想我已经弄清楚了。有一些我不知道循环关系的方法,我需要编写一个自定义的serialize 方法,而不是仅仅覆盖默认的serializeHasMany 方法。

serialize(record) {
  // Set up the main data structure for the record to be serialized
  var JSON = {
    data: {
      id: record.id,
      type: record.modelName,
      attributes: {}
    }
  };

  // Find relationships in the record and serialize them into the JSON.data.attributes object
  JSON.data.attributes = this.serializeRelationships(JSON.data.attributes, record);

  // Loop through the record's attributes and insert them into the JSON.data.attributes object
  record.eachAttribute((attr) => {
    JSON.data.attributes[attr] = record.attr(attr);
  });

  // Return the fully serialized JSON data
  return JSON;
},

// Take a parent JSON object and an individual record, loops through any relationships in the record, and creates a JSONAPI resource object
serializeRelationships(JSON, record) {
  record.eachRelationship((key, relationship) => {
    if (relationship.kind === 'hasMany') {

      // Set up the relationship data structure
      JSON[relationship.key] = {
        data: []
      };

      // Gran any relationships in the record
      var embeddedRecords = record.hasMany(relationship.key);

      // Loop through the relationship's records and build a resource object
      if (embeddedRecords) {
        embeddedRecords.forEach((embeddedRecord) => {
          var obj = {
            id: embeddedRecord.id,
            type: embeddedRecord.modelName,
            attributes: {}
          }

          // Recursively check for relationships in the record
          obj.attributes = this.serializeRelationships(obj.attributes, embeddedRecord);

          // Loop through the standard attributes and populate the record.data.attributes object
          embeddedRecord.eachAttribute((attr) => {
            obj.attributes[attr] = embeddedRecord.attr(attr);
          });

          JSON[relationship.key].data.push(obj);
        });
      }
    }
  });

  return JSON;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-11-30
    • 2017-02-14
    • 2020-07-29
    • 1970-01-01
    • 1970-01-01
    • 2015-02-26
    • 1970-01-01
    • 2013-09-14
    相关资源
    最近更新 更多