问题
I am trying to support XML responses for my Spring HATEOAS based application. JSON responses work fine as well as XML for a single resource. The problem starts with the list of the resources. Spring MVC controller cannot serialize the list built with help of ResourceAssemblerSupport derived class. The controller throws "org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation" for the curl command
curl -k -i -H "Accept:application/xml" -H "Media-Type:application/xml" -X GET http://127.0.0.1:8080/admin/roles*
My HATEOAS resource is a wrapper around entity class:
@XmlRootElement
@XmlSeeAlso(RoleModel.class)
public class RoleResource extends ResourceSupport {
public RoleModel role;
}
The controller is simple:
@RequestMapping(method = RequestMethod.GET)
public @ResponseBody HttpEntity<List<RoleResource>> getAllRoles()
throws ObjectAccessException, ObjectNotFoundException {
List<RoleModel> resp = rolesManagement.getRoles();
return new ResponseEntity<List<RoleResource>>(roleResourceAssembler.toResources(resp),
HttpStatus.OK);
}
Resource assembler class:
@Configuration
public class RoleResourceAssembler extends ResourceAssemblerSupport<RoleModel, RoleResource> {
public RoleResourceAssembler() {
super(RolesRestController.class, RoleResource.class);
}
@Bean
public RoleResourceAssembler roleResourceAssembler(){
return new RoleResourceAssembler();
}
@Override
public RoleResource toResource(RoleModel role) {
RoleResource res = instantiateResource(role);
res.role = role;
try {
res.add(linkTo(methodOn(RolesRestController.class).getRole(role.getRoleId())).withSelfRel());
} catch (ObjectAccessException | ObjectNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return res;
}
}
When I avoid ResourceAssemblerSupport and build my resources manually like this:
@XmlRootElement
@XmlSeeAlso(RoleModel.class)
public class RolesList {
private List<Resource<RoleModel>> roles;
...
}
@RequestMapping(method = RequestMethod.GET)
public @ResponseBody HttpEntity<RolesList> getAllRoles()
throws ObjectAccessException, ObjectNotFoundException {
List<RoleModel> resp = rolesManagement.getRoles();
List<Resource<RoleModel>> roles =new ArrayList<>();
for (RoleModel model: resp) {
Resource<RoleModel> res = new Resource<RoleModel>(model);
res.add(linkTo(methodOn(RolesRestController.class).getRole(model.getRoleId())).withSelfRel());
roles.add(res);
}
RolesList list = new RolesList();
list.setRoles(roles);
return new ResponseEntity<RolesList>(list,
HttpStatus.OK);
}
XML serialization works. I guess I could avoid using resource assembler and build my resources manually, but that makes the code not as clean and modular. I wonder if it is still possible to use ResourceAssemblerSupport as resource builder and return the list of resources as XML
回答1:
A resource assembler is for converting one pojo/entity/whatever to 1 hateoas resource. You are trying to convert a list to a list.
If you assembler was
public class RoleResourceAssembler extends ResourceAssemblerSupport<List<RoleModel>, RolesResource> {
and RolesResource was something that you wanted...it should work.
However i would suggest you look at using the PagedResourceAssembler which takes a page of "things" and uses an assembler to create a page of resources. The page can be the full collection or just a page in the collection. Here's a simple one for categories:
public HttpEntity<PagedResources<CategoryResource>> categories(
PagedResourcesAssembler<Category> pageAssembler,
@PageableDefault(size = 50, page = 0) Pageable pageable
){
Page<Category> shopByCategories = categoryService.findCategories(pageable);
PagedResources<CategoryResource> r = pageAssembler.toResource(shopByCategories,
this.categoryAssembler);
return new ResponseEntity<PagedResources<CategoryResource>>(r, HttpStatus.OK);
}
But..i've had some problems with jackson marshaling the PagedResources as XML...works fine for JSON.
回答2:
It seems that with ResourceAssemblerSupport it is impossible to marshal list of HATEOAS resources to XML. The reason is because the list of resources returned by toResources() method of the class extending ResourceAssemblerSupport has no @XmlRootElement and JAXB fails to marshal it. I had to create classes like
@XmlRootElement
public class Roles {
private List<RoleResource> roleResource;
....
}
@XmlRootElement
public class RoleResource extends ResourceSupport {
private RoleModel role;
...
}
and manually build my resource list. The same problem occurred when I tried to use to use Spring HATEOAS resource wrapper like
Resource<RoleModel> resource = new Resource<>();
Since Spring's Resource class is not annotated with @XmlRootElement REST controller is unable to marshal it to XML
回答3:
If your only requirement is to generate links with org.springframework.hateoas.Link and marshall as XML, the following may help.
Add a Link item to your model class.
@XmlRootElement(name="Role")
public class Roles
{
Link link;
<your Roles content>
...
}
Wrap the base class in a list in order to provide a base tag which supports XML marshalling.
@XmlRootElement(name="Roles")
public class RolesList
{
private List<Roles> rolesList;
...
<constructors>
...
@XmlElement(name="Role")
public List<Roles> getRolesList()
{
return rolesList;
}
<set/get/add methods>
}
Your controller code becomes (roughly):
@RequestMapping(method = RequestMethod.GET)
public @ResponseBody HttpEntity<RolesList> getAllRoles()
throws ObjectAccessException, ObjectNotFoundException
{
RolesList resp = new RolesList(rolesManagement.getRoles());
for (Roles r: resp)
{
r.setLink(linkTo(methodOn(RolesRestController.class).getRole(model.getRoleId())).withSelfRel());
}
return new ResponseEntity<RolesList>(resp,HttpStatus.OK);
}
来源:https://stackoverflow.com/questions/25365089/spring-hateoas-xml-serialization-for-list-of-resources-built-with-resourceassemb