I want to use the HAL format for my REST API to include embedded resources. I\'m using Spring HATEOAS for my APIs and Spring HATEOAS seems to support embedded resources; how
Pre HATEOAS 1.0.0M1: I couldn't find an official way to do this...here's what we did
public abstract class HALResource extends ResourceSupport {
private final Map<String, ResourceSupport> embedded = new HashMap<String, ResourceSupport>();
@JsonInclude(Include.NON_EMPTY)
@JsonProperty("_embedded")
public Map<String, ResourceSupport> getEmbeddedResources() {
return embedded;
}
public void embedResource(String relationship, ResourceSupport resource) {
embedded.put(relationship, resource);
}
}
then made our resources extend HALResource
UPDATE: in HATEOAS 1.0.0M1 the EntityModel (and really anything extending RepresentationalModel) this is natively supported now as long as the embedded resource is exposed via a getContent (or however you make jackson serialize a content property). like:
public class Result extends RepresentationalModel<Result> {
private final List<Object> content;
public Result(
List<Object> content
){
this.content = content;
}
public List<Object> getContent() {
return content;
}
};
EmbeddedWrappers wrappers = new EmbeddedWrappers(false);
List<Object> elements = new ArrayList<>();
elements.add(wrappers.wrap(new Product("Product1a"), LinkRelation.of("all")));
elements.add(wrappers.wrap(new Product("Product2a"), LinkRelation.of("purchased")));
elements.add(wrappers.wrap(new Product("Product1b"), LinkRelation.of("all")));
return new Result(elements);
you'll get
{
_embedded: {
purchased: {
name: "Product2a"
},
all: [
{
name: "Product1a"
},
{
name: "Product1b"
}
]
}
}
here is a small example what we've found. First of all we use spring-hateoas-0.16
Imaging we have GET /profile
that should return user profile with embedded emails list.
We have email resource.
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
@Relation(value = "email", collectionRelation = "emails")
public class EmailResource {
private final String email;
private final String type;
}
two emails that we want to embedded into profile response
Resource primary = new Resource(new Email("neo@matrix.net", "primary"));
Resource home = new Resource(new Email("t.anderson@matrix.net", "home"));
To indicate that these resources are embedded we need an instance of EmbeddedWrappers:
import org.springframework.hateoas.core.EmbeddedWrappers
EmbeddedWrappers wrappers = new EmbeddedWrappers(true);
With the help of wrappers
we can create EmbeddedWrapper
instance for each email and put them into a list.
List<EmbeddedWrapper> embeddeds = Arrays.asList(wrappers.wrap(primary), wrappers.wrap(home))
The only thing is left to do is to construct our profile resource with these embeddeds. In the example below I use lombok to short the code.
@Data
@Relation(value = "profile")
public class ProfileResource {
private final String firstName;
private final String lastName;
@JsonUnwrapped
private final Resources<EmbeddedWrapper> embeddeds;
}
Keep in mind annotation @JsonUnwrapped
on embeddeds field
And we are ready to return all this from controller
...
Resources<EmbeddedWrapper> embeddedEmails = new Resources(embeddeds, linkTo(EmailAddressController.class).withSelfRel());
return ResponseEntity.ok(new Resource(new ProfileResource("Thomas", "Anderson", embeddedEmails), linkTo(ProfileController.class).withSelfRel()));
}
Now in the response we'll have
{
"firstName": "Thomas",
"lastName": "Anderson",
"_links": {
"self": {
"href": "http://localhost:8080/profile"
}
},
"_embedded": {
"emails": [
{
"email": "neo@matrix.net",
"type": "primary"
},
{
"email": "t.anderson@matrix.net",
"type": "home"
}
]
}
}
Interesting part in using Resources<EmbeddedWrapper> embeddeds
is that you can put different resources in it and it will automatically group them by relations. For this we use annotation @Relation
from org.springframework.hateoas.core
package.
Also there is a good article about embedded resources in HAL