jsonapi-serializer icon indicating copy to clipboard operation
jsonapi-serializer copied to clipboard

transforming not possible for relationships

Open HoneyryderChuck opened this issue 8 years ago • 3 comments

I'm using the transform callback with success when setting up a serializer attribute. However, when I pass the same option to the relationship, it just isn't called.


const PersonSerializer = new Serializer('persons', {
	attributes: ['id', 'name', 'isAlive', 'relatives'],
        transform: (person) => {
          person.isAlive = !person.deadAt
          return person
        },
        // works
        relatives: {
          attributes: ['id', 'name', 'isAlive'],
          transform: (person) => {
            person.isAlive = !person.deadAt
            return person
          },
          // doesn't work
}

this might be because the feature doesn't exist, but I'd say it should, according to the principle of least surprise.

Also, I can't seem to pass a serializer to a relationship (using the example above, having a RelativeSerializer and somehow passing it to serialize the relationship). Is this actually possible or in the roadmap?

HoneyryderChuck avatar May 05 '17 11:05 HoneyryderChuck

I solved this problem in our app by recursing down both the data and the serialization config in the main transform via the following function.

/**
 * Transform this level and recurse into all attributes that have
 * nested models and transform there as well
 */
export function transformRecursively(model, record) {
  // Only Transform on objects not arrays of objects
  if (Array.isArray(record)) {
    return record.forEach(rec => transformRecursively(model, rec));
  }

  //Act
  if (model && model.transform && typeof model.transform === 'function' && record) {
    model.transform(record);
  }

  //Recurse on attributes that have model objects
  if (record && model && model.attributes) {
    model.attributes.forEach((key) => {
      if (model[key]) {
          transformRecursively(model[key], record[key]);
      }
    })
  }
}

And then in the main transform calling it via:

// Run transformations recursively on any sub-modules that have a transform function
this.attributes.forEach((key) => {
  transformRecursively(this[key], record[key]);
});

Its been working out for us thus far but your mileage might vary.

psheraton avatar Jan 25 '19 21:01 psheraton

@SeyZ are you open to PRs for this? I ran into this as well. I think it'd make a nice addition to an already great package.

allthesignals avatar Apr 28 '20 02:04 allthesignals

As I discovered while looking into the same issue, you can transform relationships by just digging deeper into the primary resource's properties. To use the example above:

const PersonSerializer = new Serializer('persons', {
    attributes: ['id', 'name', 'isAlive', 'relatives'],
    transform: (person) => {
        // transform primary
        person.isAlive = !person.deadAt

        // transform relationship
        const relatives = person.relatives
        relatives = relatives.map(relative => {
            relative.person.isAlive = !relatives.person.deadAt
            return relative
        })

        // return primary
        return person
        },
    relatives: {
        attributes: ['id', 'name', 'isAlive'],
    }
}

wayneoco avatar Nov 24 '23 18:11 wayneoco