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

Deserialization - automatically discover and process relationships

Open jamesdixon opened this issue 9 years ago • 14 comments

Hi,

Great work on this library as always.

I was just curious if there was a reason that when deserializing, relationships cannot be automatically deserialized? I'm a bit confused at the need for the additional function.

Best, James

jamesdixon avatar Mar 20 '16 05:03 jamesdixon

I am also wondering. For now it's much easier for me to grab them from non-deserialized response.

thehappycoder avatar Mar 21 '16 14:03 thehappycoder

Hi @SeyZ - any thoughts on this? I'd be more than willing to help out.

jamesdixon avatar Mar 24 '16 20:03 jamesdixon

Hey @jamesdixon !

Sorry for late! I'm not sure to understand what means "deserialize automatically"?

Here's a sample usecase: • book has one author

{
  "data" {
    "type": "books",
    "id": "1",
    "attributes": {
    },
    "relationships": {
      "author": {
        "data" {
          "type": "users",
          "id": "2"
        }
      }
    }
  }
}

• When deserializing a book, I would like to fetch its author from my database.

new JSONAPIDeserializer({
  users: {
    valueForRelationship: function (relationship) {
       return Author.findById(relationship.id); // In this example, a Promise is returned.
    }
  }
}).deserialize(jsonapi, function (err, book) {
  // …
});

I'm not sure to understand how can I do that automatically?

SeyZ avatar Mar 24 '16 21:03 SeyZ

No worries, @SeyZ!

That's a slightly different, but valid, use case than I was considering. What I was expecting was that relationships would be deserialized regardless if there is a valueForRelationship option set.

So taking your example, I would see the following if I did not provide a valueForRelationship:

{
  "id": 1,
  "authors": [ 2 ]
}

My thought that is that this would provide a bit more flexibility in being able to handle the relationship. In my case, I want to automatically deserialize every JSON API response using a plugin and then would like to act accordingly on any relationships rather than having to specify a deserializer per controller/handler. Let me know if that makes sense :)

Cheers.

jamesdixon avatar Mar 24 '16 22:03 jamesdixon

Hey @jamesdixon !

Yeah it makes sense! Providing the default behavior like you described is good! Thanks for tips!

SeyZ avatar Mar 26 '16 09:03 SeyZ

@SeyZ I was going to take a look at this tomorrow. Is it trivial to implement? If so, would be great if you could point me in the right direction. Otherwise, I'll dig into it. Thanks!

jamesdixon avatar Apr 04 '16 04:04 jamesdixon

@SeyZ thinking about this a bit more, I'm thinking it might make more sense to parse the data separately into attributes and relationships. Reason being is that when the data is deserialized, it makes it much simpler to manipulate if you need to do something specific depending on what that data is.

For example, I'm using the Bookshelf ORM -- to retrieve relationships along with primary data, I need to pass in a withRelated parameter. If relationships are deserialized as in my example above, I need to specifically pull out those relationships and then create the proper withRelated parameter to send to Bookshelf. However, if the relationships are deserialized into a specific key, I can automatically craft the withRelated parameter by just accessing what's under the relationships key. So, my proposal would be for the following structure:

{
  attributes: {
    id: 1
  },
  relationships: {
    authors: [ 2 ]
  }
}

I know I've mentioned automation a number of times, but I think it's important to be able to use a library such as this to automatically serialize and deserialize payloads with little to no intervention on the developer's part. Although the serialization/deserialization is fairly automatic, it still requires user intervention in most cases. Ultimately, I believe we can get to a point where user intervention is rare.

@thehappycoder do you have any thoughts on this?

jamesdixon avatar Apr 10 '16 18:04 jamesdixon

@jamesdixon putting attributes and relationships under different keys might be desired in some use cases but I think in most just a flat javascript object is needed.

But I really like your proposal about default deserializiation of relationships. Did you had any idea about how to handle one-to relationships? It shouldn't be wrapped in array but how to know if it's a many-to with just a single value or an one-to relationship?

jelhan avatar Apr 23 '16 11:04 jelhan

@jelhan it's quite possible that it isn't the most common of cases -- would be good to hear from others.

As for handling the relationships, I believe the spec defines that even a single value many-to relation should be wrapped in an array. That said, we should be able to look to see if it's an array, if so, it's many-to, otherwise it's a one-to. Thoughts?

jamesdixon avatar Apr 24 '16 00:04 jamesdixon

@jamesdixon you are right:

Resource linkage MUST be represented as one of the following:

  • null for empty to-one relationships.
  • an empty array ([]) for empty to-many relationships.
  • a single resource identifier object for non-empty to-one relationships.
  • an array of resource identifier objects for non-empty to-many relationships.

http://jsonapi.org/format/#document-resource-object-linkage

We could stick with these definition if resource linkage is used. If not (relationships are only represented by links) we could use undefined.

A default behaviour to automatically deserialize all relationships would reduce implementation complexity a lot - at least for my use case.

jelhan avatar Apr 25 '16 14:04 jelhan

@jelhan agreed and it's a fairly simple test.

A default behaviour to automatically deserialize all relationships would reduce implementation complexity a lot - at least for my use case.

This would do the same for me. What's your data access layer look like (ORM?)? I ask because I'm curious about the level of pre-processing that could be implemented and would satisfy the most cases.

jamesdixon avatar Apr 30 '16 20:04 jamesdixon

@jamesdixon I'm working on a hook for Sails.js framework which uses Waterline.js as ORM: sails-hook-jsonapi. It's still in an early stage.

jelhan avatar May 02 '16 08:05 jelhan

Very nice. So, how would you go about creating a relationship using Waterline? For example, here's a sample payload from one of my tests:

{
    data: {
        type: 'appointment',
        attributes: {
            startsAt: '2016-06-19 10:30:00.000+00',
            endsAt: '2016-06-19 11:00:00.000+00',
            recurrencePattern: null,
            businessNotes: 'Be careful -- Critter likes to try and get out the door when it\'s opened.',
            customerNotes: 'Critter has been having the shits. Keep an eye on him.'
        },
        relationships: {
            pets: {
                data: [{
                    type: 'pet',
                    id: 1
                }]
            },
            services: {
                data: [{
                    type: 'service',
                    id: 1
                }]
            },
            surcharges: {
                data: [{
                    type: 'surcharge',
                    id: 1
                }]
            },
            staff: {
                data: {
                    type: 'staff',
                    id: 1
                }
            },
            customer: {
                data: {
                    type: 'customer',
                    id: 1
                }
            }
        }
    }
}
}

How would you handle that so the main payload is created in addition to all of the relationships, which could be one-to or many?

I ask because I'm curious about the various approaches for getting the data where it needs to go.

jamesdixon avatar May 02 '16 09:05 jamesdixon

Sails.js documentation for Models and ORM but I'm not directly working with ORM but just normalize requests for and responses from default blueprints.

jelhan avatar May 02 '16 09:05 jelhan