Spring & Jackson @JsonView

Todays problem:

  • at least 2 different APIs (CRUD / Bulk)
  • non-changed code-paths / REST-Endpoints should work like before
  • they should show a different set of attributes in the same object

First Try – Jackson mixins

Mixins have to be registered in the global ObjectMapper that also includes the Spring-MVC instance and is therefore used by nearly the whole application.

The first requirement is no problem if we are only using two different sets of different views.

The second requirement should also be no real Problem because mixins are registered onto a specific type

objectMapper.addMixIn(User.class, BulkUser.class)

This makes it quite handy because you can build a simple output-class for every endpoint that should be handled differently. If we use a simple example based on a fictional User class for example:

class BulkUser extends User {
}
interface BulkUserMixin {
    @JsonIgnore
    String getUsername();
}

This makes mixins really handy to quickly change the system-wide representation of a specific class.

The Problem: Mixins only work for specifc Types and not for subtypes.

Therefore, if the User or for that matter the BulkUser have a getter like:

class User {
// ...
    Address getAddress();
// ...
}

the nested class Address would not be directly affected without registering a mixin for that exact class.

Conclusion: Because the attributes that should be hidden from the bulk api are nested in types, that are also used in the „normal“ api that should expose everything, mixins are not the tool of choice.

Second Idea

The second idea that has presented itself after debugging the first try with mixins is to replace the now used APIs for accessing the User and Address.

This would be to use POJOs (Plain-Old-Java-Object) with seperately implemented DTOs (Data-Transfer-Object) to build completely seperated views on the fundamental objects.

After a quick counting on the positions that directly one of these objects, this idea was not implemented because time is essential in this stage of the associated project.

My Solution – @JsonView

After some digging around at the com.fasterxml.jackson.annotation package there seemed another interesting concept that could be implemented:

@JsonView

This annotation can be used to build seperate views of the same object.

If we take our example from the first try and slightly modify it, then we’re able to simple show different representations of that object without any big modifications.

class User {
    Long getId();

    @JsonView(ViewRepo.API.class)
    String getUsername();

    @JsonView({ViewRepo.API.class, ViewRepo.Bulk.class})
    Address getAddress();
}

With this simple annotation at the model-level we can decide on which call we want to display what attribute. But to get it fully working we need the same annotation also on the method that is called via REST.

@RestController
public class ApiEndpoint {

    @GetMapping("/api")
    @JsonView(ViewRepo.API.class)
    public Object showUsers() {
        // ....
    }
}

@RestController
public class BulkEndpoint {

    @GetMapping("/bulk")
    @JsonView(ViewRepo.Bulk.class)
    public Object showUsers() {
        // ....
    }
}

Now if we call out REST-Endpoint /bulk we are presented with an object that should lacks the username attribute but includes everything else.

{
  "id": 1,
  "address": {
    "country": "Germany",
    /* .... */
  }
}

For contrast our /api should still include everything. This works because everything that is not annotated at all, is still serialized every time.

And for everyone who wonders what the class (or interface) ViewRepo looks like:

public interface ViewRepo {
    interface API {}
    interface Bulk {}
}

And yeah, you can absoutely use inheritance here to include one view into another one with something like the following.

public interface ViewRepo {
    interface API {}
    interface Bulk {} // This is used by normal users
    interface AdminBulk extends Bulk {} // Used to show more
}

Afterword

This was just another day in the life of a mainly Java programmer who just had some time to write a „whole“ article instead of just posting some random code-snippets.


Beitrag veröffentlicht

in

, , ,

von

Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert