I'm a bit confused about serializers

I have a data source that returns JSON in something of a mix between what the documentation describes as the JSONSerializer and JSONAPISerializer.

The return has a root attribute but no type information:

{"data":[
	{"id":1,"name":"Blue"},
	{"id":4,"name":"Red"},
	{"id":5,"name":"Green"},
	{"id":3,"name":"Yellow"},
	{"id":2,"name":"Purple"}
	]
}

This obviously produces a problem with either of the serializers because it has a root (which the same across all models - it is always data) but no type information. The root causes it to error for the other serializer because it doesn’t expect one to be there.

Changing the data source isn’t really an option for me but the only way I can see forward is having to make a custom serializer for each model in the application to account for the root and lack of type. I really don’t want to do that!

Any advice appreciated

I don’t have specifics, but with that kind of data, you’d need to make either make a serializer/adapter per-model that sets the type, or derive the type from the URL or model class.

You’d want to use the JSONSerializer, as the JSONAPISerializer is for jsonapi.org formatted json payloads.

If you haven’t seen this yet, it’s very useful: https://guides.emberjs.com/release/models/customizing-serializers/#toc_customizing-serializers

Note that ember-data uses jsonapi.org -formatted data internally, so when you translate your data from your API, you’ll be building a jsonapi.org Document.

1 Like

Yes, serializers are unfortunately described in a confusing way and have a lot of API surface that I would honestly prefer to deprecate. There is a very clear and simple way to understand and use serializers, but it’s hard to see from looking at the docs and guides.

The rules are simple:

  1. Always extend from JSONAPISerializer. It’s quite irrelevant what the shape of your backend data is, just always use JSONAPISerializer. The reason I say this is that there is no spec describing what constitutes the format(s) JSONSerializer supports, so it’s hard to be sure you’re doing things right. Whereas the JSONAPI spec is very clear.

  2. The only methods you truly need to understand and possibly implement are:

    • normalizeResponse: receives whatever your server sends, and must return a standard JSON:API document.
    • serialize: receives a standard JSON:API document, and must return the format you want to actually send to your server.

Using serializers in this way, your job is clear: write a function that converts your custom server output into a JSON:API compliant format.

4 Likes

Thanks for that, putting serializers in context like that was really helpful.

In the end it was much simpler to bypass the root object and return payload.data in the args of normalizeResponse:

    normalizeResponse(store, primaryModelClass, payload, id, requestType) {      
         return this._super(store, primaryModelClass, payload.data, id, requestType);
    }

At least at this early, scaffolding phase of my project.

After getting some useful input from @runspired I can add some more details to my recommendations above.

If you are using custom attribute transforms, be sure to call this._super in your methods. Keep in mind that the normalizeResponse method on JSONAISerializer expects valid JSON:API as input, so you would turn your arbitrary server response into JSON:API first, then pass that into _super, then return the result.

If you’re not relying on that kind of behavior, you can do an even simpler thing and extend from Ember.Object, using no serializer base class at all. You don’t actually need a base class as long as you implement the two required methods. This gives you total control over what happens between your server format and standard JSON:API format.

1 Like