Spring HATEOAS客户端忽略空链接-“;模板不能为null或空&引用;例外
我正在访问由第三方提供的HATEOAS API,出于某种原因,我们从他们那里收到的响应包含href的空值链接。这会引发异常。我无法更改响应,因为我无法控制此API。有办法解决这个问题吗 下面是JSON的示例:Spring HATEOAS客户端忽略空链接-“;模板不能为null或空&引用;例外,spring,spring-boot,spring-hateoas,Spring,Spring Boot,Spring Hateoas,我正在访问由第三方提供的HATEOAS API,出于某种原因,我们从他们那里收到的响应包含href的空值链接。这会引发异常。我无法更改响应,因为我无法控制此API。有办法解决这个问题吗 下面是JSON的示例: { "_embedded": { "example": [{ ... }] } "_links": { "next": { &qu
{
"_embedded": {
"example": [{ ... }]
}
"_links": {
"next": {
"href": null
},
"prev": {
"href": null
},
"self": {
"href": "https://bag.basisregistraties.overheid.nl/api/v1/panden"
}
}
}
当我使用RestTemplate发出请求时,我得到一个IllegalArgumentException,消息为“Template不得为null或空!”
在花了一些时间查看Jackson2HalModule的代码后,我找到了这个问题的解决方案。我无法使用任何配置来告诉库忽略具有空值的链接。相反,我可以做的是重写deserialize()方法,这样我就可以添加null检查并避免问题 为了做到这一点,我必须创建新的链接反序列化器类。它需要扩展Jackson2HalModule.HalLinkListDeserializer,然后我只需从库源代码中复制并粘贴代码,并进行所需的修改
public class CustomHalLinkListDeserializer extends Jackson2HalModule.HalLinkListDeserializer {
@Override
public List<Link> deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
List<Link> result = new ArrayList<Link>();
String relation;
// links is an object, so we parse till we find its end.
while (!JsonToken.END_OBJECT.equals(jp.nextToken())) {
if (!JsonToken.FIELD_NAME.equals(jp.getCurrentToken())) {
throw new JsonParseException(jp,
"Expected relation name", jp.getCurrentLocation());
}
// save the relation in case the link does not contain it
relation = jp.getText();
if (JsonToken.START_ARRAY.equals(jp.nextToken())) {
while (!JsonToken.END_ARRAY.equals(jp.nextToken())) {
CustomLink link = jp.readValueAs(CustomLink.class);
String href = link.getHref() != null ? link.getHref() : "http://dummy";
result.add(new Link(href, relation));
}
} else {
CustomLink link = jp.readValueAs(CustomLink.class);
String href = link.getHref() != null ? link.getHref() : "http://dummy";
result.add(new Link(href, relation));
}
}
return result;
}
}
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, false);
Jackson2HalModule jackson2HalModule = new Jackson2HalModule();
mapper.registerModule(jackson2HalModule);
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setSupportedMediaTypes(MediaType.parseMediaTypes(Arrays.asList("application/json;charset=UTF-8", "application/hal+json")));
converter.setObjectMapper(mapper);
RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder();
RestTemplate restTemplate = restTemplateBuilder.messageConverters(Collections.singletonList(converter)).build();
HttpHeaders headers = new HttpHeaders();
headers.set("Accept", "application/json, application/*+json");
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
HttpEntity<String> entity = new HttpEntity(entity.toString(), headers);
ParameterizedTypeReference<Resources<Panden>> typeRefDevices = new ParameterizedTypeReference<>() {};
ResponseEntity<Resources<Panden>> result = restTemplate.exchange(endpoint, HttpMethod.POST, entity, typeRefDevices);;
public class CustomHalLinkListDeserializer extends Jackson2HalModule.HalLinkListDeserializer {
@Override
public List<Link> deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
List<Link> result = new ArrayList<Link>();
String relation;
// links is an object, so we parse till we find its end.
while (!JsonToken.END_OBJECT.equals(jp.nextToken())) {
if (!JsonToken.FIELD_NAME.equals(jp.getCurrentToken())) {
throw new JsonParseException(jp,
"Expected relation name", jp.getCurrentLocation());
}
// save the relation in case the link does not contain it
relation = jp.getText();
if (JsonToken.START_ARRAY.equals(jp.nextToken())) {
while (!JsonToken.END_ARRAY.equals(jp.nextToken())) {
CustomLink link = jp.readValueAs(CustomLink.class);
String href = link.getHref() != null ? link.getHref() : "http://dummy";
result.add(new Link(href, relation));
}
} else {
CustomLink link = jp.readValueAs(CustomLink.class);
String href = link.getHref() != null ? link.getHref() : "http://dummy";
result.add(new Link(href, relation));
}
}
return result;
}
}
public abstract class CustomResourceSupportMixin extends ResourceSupport {
@Override
@XmlElement(name = "link")
@JsonProperty("_links")
@JsonInclude(JsonInclude.Include.NON_EMPTY)
@JsonSerialize(using = Jackson2HalModule.HalLinkListSerializer.class)
@JsonDeserialize(using = CustomHalLinkListDeserializer.class)
public abstract List<Link> getLinks();
}
ObjectMapper mapper = new ObjectMapper();
Jackson2HalModule jackson2HalModule = new Jackson2HalModule();
jackson2HalModule.setMixInAnnotation(ResourceSupport.class,
CustomResourceSupportMixin.class);
mapper.registerModule(jackson2HalModule);