Reputation: 678
I have a sample response class that extends RepresentationModel. In some scenarios I don't add any hateoas links in the response. In that case, I'm getting an empty links field in the json response
"links": []
I tried adding "JsonInclude.Include.NON_EMPTY" to the response class, but since the links field is final in RepresentationModel, it's still bringing empty links field in the response.
How can I avoid this empty links field in the response ?
Upvotes: 3
Views: 2865
Reputation: 1005
update for 2024,with openapi generator for Spring boot i use the swagger.yaml, then on pom.xml, I just added the annotation Jackson non empty it works:
<additionalModelTypeAnnotations>@com.fasterxml.jackson.annotation.JsonInclude(com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY)
</additionalModelTypeAnnotations>
After that my links that are empty are not put in the Json created
Upvotes: 0
Reputation: 2419
Firstly make sure you have a good reason to use links with media type application/json
rather than media type built for hypermedia such as HAL (application/hal+json
).
Though RepresentationModel
has a field of List<Link>
, the getter returns a Links
instead of List<Link>
. Jackson treats it as simple type (where a JsonSerializer
is used) instead of a collection type (where a CollectionSerializer
is used), so JsonInclude.Include.NON_EMPTY
doesn't work as you expect.
public class RepresentationModel<T extends RepresentationModel<? extends T>> {
private final List<Link> links;
@JsonProperty("links")
public Links getLinks() {
return Links.of(links);
}
}
public class Links implements Iterable<Link> { }
public abstract class JsonSerializer<T> {
public boolean isEmpty(SerializerProvider provider, T value) {
return (value == null);
}
}
public class CollectionSerializer {
@Override
public boolean isEmpty(SerializerProvider prov, Collection<?> value) {
return value.isEmpty();
}
}
One solution is override the getter getLinks()
and use a customm filter.
class User extends RepresentationModel<User> {
// ...
@JsonProperty("links")
// if links is an empty JSON array, exclude it
@JsonInclude(value = JsonInclude.Include.CUSTOM, valueFilter = EmptyLinksFilter.class)
@Override
public Links getLinks() {
return super.getLinks();
}
}
/* The word "filter" is a bit ambiguous (included? or excluded?).
Here when the equals() of this class return true, the value will be excluded.
Choose a class name to make yourself comfortable. */
class EmptyLinksFilter{
@Override
public boolean equals(Object obj) {
if (obj == null || !(obj instanceof Links)) {
return false;
}
Links links = (Links) obj;
return links.isEmpty();
}
}
The full code is in Github.
Second solution may be custom mixin like what Spring HATEOAS already build for HAL. Related code are:
The second solution is much complicated. That's why I recommand media types like HAL, for which Spring HATEOAS already has good configuration.
Upvotes: 2
Reputation: 678
As per the answer from @yejianfengblue, I have created a custom representation model as below and extended this CustomRepresentationModel
from response java classes instead of Hateoas RepresentationModel
.
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.springframework.hateoas.Links;
import org.springframework.hateoas.RepresentationModel;
import org.springframework.lang.NonNull;
public class CustomRepresentationModel<T extends CustomRepresentationModel<? extends T>> extends
RepresentationModel<T> {
@JsonProperty("_links")
@JsonInclude(value = JsonInclude.Include.CUSTOM, valueFilter = NonEmptyLinksFilter.class)
@NonNull
@Override
public Links getLinks() {
return super.getLinks();
}
static class NonEmptyLinksFilter {
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Links)) {
return false;
}
Links links = (Links) obj;
return links.isEmpty();
}
@Override
public int hashCode() {
return super.hashCode();
}
}
}
Upvotes: 1