I have a REST API specification that talks with back-end microservices, which return the following values:
On "collections" responses (e.g. GET /users) :
{
users: [
{
... // single user object data
}
],
links: [
{
... // single HATEOAS link object
}
]
}
On "single object" responses (e.g. GET /users/{userUuid}
) :
{
user: {
... // {userUuid} user object}
}
}
This approach was chosen so that single responses would be extensible (for example, maybe if GET /users/{userUuid}
gets an additional query parameter down the line such at ?detailedView=true
we would have additional request information).
Fundamentally, I think it is an OK approach for minimizing breaking changes between API updates. However, translating this model to code is proving very arduous.
Let's say that for single responses, I have the following API model object for a single user:
public class SingleUserResource {
private MicroserviceUserModel user;
public SingleUserResource(MicroserviceUserModel user) {
this.user = user;
}
public String getName() {
return user.getName();
}
// other getters for fields we wish to expose
}
The advantage of this method is that we can expose only the fields from the internally used models for which we have public getters, but not others. Then, for collections responses I would have the following wrapper class:
public class UsersResource extends ResourceSupport {
@JsonProperty("users")
public final List<SingleUserResource> users;
public UsersResource(List<MicroserviceUserModel> users) {
// add each user as a SingleUserResource
}
}
For single object responses, we would have the following:
public class UserResource {
@JsonProperty("user")
public final SingleUserResource user;
public UserResource(SingleUserResource user) {
this.user = user;
}
}
This yields JSON
responses which are formatted as per the API specification at the top of this post. The upside of this approach is that we only expose those fields that we want to expose. The heavy downside is that I have a ton of wrapper classes flying around that perform no discernible logical task aside from being read by Jackson to yield a correctly formatted response.
My questions are the following:
How can I possibly generalize this approach? Ideally, I would like to have a single
BaseSingularResponse
class (and maybe aBaseCollectionsResponse extends ResourceSupport
class) that all my models can extend, but seeing how Jackson seems to derive the JSON keys from the object definitions, I would have to user something likeJavaassist
to add fields to the base response classes at Runtime - a dirty hack that I would like to stay as far away from as humanly possible.Is there an easier way to accomplish this? Unfortunately, I may have a variable number of top-level JSON objects in the response a year from now, so I cannot use something like Jackson's
SerializationConfig.Feature.WRAP_ROOT_VALUE
because that wraps everything into a single root-level object (as far as I am aware).Is there perhaps something like
@JsonProperty
for class-level (as opposed to just method and field level)?