I am trying to invoque very simple json webservices that return data of this form:
{
"_embedded": {
"users": [{
"identifier": "1",
"firstName": "John",
"lastName": "Doe",
"_links": {
"self": {
"href": "http://localhost:8080/test/users/1"
}
}
},
{
"identifier": "2",
"firstName": "Paul",
"lastName": "Smith",
"_links": {
"self": {
"href": "http://localhost:8080/test/users/2"
}
}
}]
},
"_links": {
"self": {
"href": "http://localhost:8080/test/users"
}
},
"page": {
"size": 20,
"totalElements": 2,
"totalPages": 1,
"number": 0
}
}
As you can see, it is pretty straight forward. I have no problems parsing the links, having my POJOs extending form ResourceSupport. Here is what they look like:
UsersJson (the root element)
public class UsersJson extends ResourceSupport {
private List<UserJson> users;
[... getters and setters ...]
}
UserJson
public class UserJson extends ResourceSupport {
private Long identifier;
private String firstName;
private String lastName;
[... getters and setters ...]
}
The thing is that I was expecting jackson and spring to be smart enough to parse the _embedded property and populate my UsersJson.users attribute but it isn't.
I tried various things I found on the internet but the only thing I could get to work properly was to create a new class acting as an _embedded wrapper:
UsersJson (the root element)
public class UsersJson extends ResourceSupport {
@JsonProperty("_embedded")
private UsersEmbeddedListJson embedded;
[... getters and setters ...]
}
Embedded "wrapper"
public class UsersEmbeddedListJson extends ResourceSupport {
private List<UserJson> users;
[... getters and setters ...]
}
It works but I find it quite ugly.
Yet I though the following configuration of the RestTemplate would have worked (especially when I saw EmbeddedMapper in Jackson2HalModule), but it didn't:
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.registerModule(new Jackson2HalModule());
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setSupportedMediaTypes(MediaType.parseMediaTypes("application/hal+json"));
converter.setObjectMapper(mapper);
RestTemplate restTemplate = new RestTemplate(Collections.singletonList(converter));
ResponseEntity<UsersJson> result = restTemplate.getForEntity("http://localhost:8089/test/users", UsersJson.class, new HashMap<String, Object>());
System.out.println(result);
Can somebody tell me what I am missing?
Finally, I found a better a way to consume those application/hal+json APIs.
Spring hateoas actually provides a client almost ready to use: org.springframework.hateoas.client.Traverson.
Traverson traverson = new Traverson(new URI("http://localhost:8080/test"), MediaTypes.HAL_JSON);
TraversalBuilder tb = traverson.follow("users");
ParameterizedTypeReference<Resources<UserJson>> typeRefDevices = new ParameterizedTypeReference<Resources<UserJson>>() {};
Resources<UserJson> resUsers = tb.toObject(typeRefDevices);
Collection<UserJson> users= resUsers .getContent();
As you can see, I got rid UsersJson and UsersEmbeddedListJson.
Here are the maven dependencies I added
<dependency>
<groupId>org.springframework.hateoas</groupId>
<artifactId>spring-hateoas</artifactId>
<version>0.19.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.plugin</groupId>
<artifactId>spring-plugin-core</artifactId>
<version>1.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.0.0</version>
</dependency>
Had to add this to my DTO:
@JsonProperty("_links")
public void setLinks(final Map<String, Link> links) {
links.forEach((label, link) -> add(link.withRel(label)) );
}
since ResourceSupport has no POJO standard/Json-signaled setter/constructor for links
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With