在春季启动 restapi 响应中排除空的 Hateoas "links"



我有一个扩展RepresentationModel的示例响应类。在某些情况下,我不会在响应中添加任何hateoas链接。在这种情况下,我在json响应中得到一个空的链接字段

"链接":[]

我尝试添加";JsonInclude.Include.NON_EMPTY";到响应类,但由于链接字段是RepresentationModel中的最终字段,因此它仍然在响应中引入空的链接字段。

如何避免响应中出现此空链接字段?

首先确保您有充分的理由使用媒体类型为application/json的链接,而不是为HAL(application/hal+json(等超媒体构建的媒体类型。

虽然RepresentationModel的字段为List<Link>,但getter返回的是Links而不是List<Link>。Jackson将其视为简单类型(使用JsonSerializer(,而不是集合类型(使用CollectionSerializer(,因此JsonInclude.Include.NON_EMPTY无法按预期工作。

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();
}
}

一种解决方案是覆盖gettergetLinks()并使用customm过滤器。

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();
}
}

完整的代码在Github中。

第二个解决方案可能是自定义的mixin,就像Spring HATEOAS已经为HAL构建的一样。相关代码为:

  • RepresentationModelMixin
  • Jackson2HalModule.HalLinkListSerializer
  • 插孔2半模块
  • HalMediaType配置

第二个解决方案要复杂得多。这就是为什么我推荐像HAL这样的媒体类型,Spring HATEOAS已经为其提供了良好的配置。

根据@yejianfengblue的回答,我创建了一个如下所示的自定义表示模型,并从响应java类而不是HateoasRepresentationModel扩展了这个CustomRepresentationModel

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();
}
}
}

最新更新