Spring renoas xml serialization for resource list generated with ResourceAssemblerSupport

I am trying to maintain XML responses for my HATEOAS based Spring application. JSON responses work fine, and also XML for a single resource. The problem starts with a list of resources. Spring MVC Controller cannot serialize a list built with ResourceAssemblerSupport derived class. Controller is throwing "org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable view" for 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 the 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 manually create my resources 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 think I could avoid using resource assembler and create resources manually, but that makes the code less clean and modular. I wonder if it is possible to use ResourceAssemblerSupport as a resource composer and return a list of resources as XML

0


source to share


3 answers


The resource assembler is designed to convert one pojo / entity / any object to 1 renoas resource. You are trying to convert a list to a list.

If you were an assembler

public class RoleResourceAssembler extends ResourceAssemblerSupport<List<RoleModel>, RolesResource> {

      

and RolesResource is what you wanted ... it should work.



However, I would suggest you take a look at using the PagedResourceAssembler, which takes a "things" page and uses an assembler to create a resource page. A page can be a complete collection or just a page within a 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 had some problems with jackson marshaling PagedResources as XML ... works fine for JSON.

0


source


It looks like there is no way to concatenate the HATEOAS resource list for XML with ResourceAssemblerSupport. The reason is that the list of resources returned by the toResources () method of the class extending ResourceAssemblerSupport does not have @XmlRootElement, and JAXB cannot 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 create the resource list. The same problem came up when I tried to use the Spring HATEOAS resource wrapper like



Resource<RoleModel> resource = new Resource<>();

      

Since Spring resource class is not annotated with @XmlRootElement, the REST controller cannot marshal it to XML

0


source


If your only requirement is to create links with org.springframework.hateoas.Link and marshall as XML this might help.

Add a link element to the model class.

@XmlRootElement(name="Role")
public class Roles
{
   Link link;
   <your Roles content>
   ...
}

      

Wrap the base class in a list to provide a base tag that supports XML sorting.

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

      

0


source







All Articles