Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JAX-RS Response for XML or JSON is not working

I have the following GenericRest class which I use to extend rest classes which are based on Entity classes annotated with @XmlRootElement.

public class GenericRest<T extends BaseEntity> {

    @Inject @Service
    GenericService<T> service;

    public GenericService<T> getService() {
        return service;
    }

    @GET
    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
    public Response getAll() {
        // This works for JSON but does not work for XML Requests.
        List<T> list = getService().findAll();
        // This just gives the attributes for the BaseEntity.
        //GenericEntity<List<T>> list = new GenericEntity<List<T>>(getService().findAll()) {};
        return Response.ok(list).build();
    }
}

The APPLICATION_JSON is working fine in the currently uncommented situation, but the APPLICATION_XML give the error:

Could not find MessageBodyWriter for response object of type: java.util.ArrayList of media type: application/xml

The commented situation works fine for both MediaTypes, but returns just the attributes of the BaseEntity, not the added attributes for the extended classes. How can I get the attributes for the extended classes and have both MediaTypes working?

Complete repository can be found here (work in progress): https://github.com/martijnburger/multitenant

=== Update 1 ===

I changed the @XmlSeeAlso annotation on the Entities. It was on the specific entities, but needed to be on the BaseEntity. Further I used the GenericList implementation above. This gives correct XML responses. However, it still returns only the BaseEntity attributes in JSON repsonses. I have two followup questions:

  1. How to return a JSON response including the attributes for the specific object that is requested?
  2. I would prefer it if the BaseEntity does not have to be touched when adding or removing specific Entity classes. Because the @XmlSeeAlso annotation every time I add a new Entity class, I need to update the annotation. Is there another method to implement this where I do not need to touch the BaseEntity?

Repository with changes can be found here: https://github.com/martijnburger/multitenant/tree/so_36291250

=== Update 2 ===

I had good hope that the @JsonSubTypes annotation from Jackson would solve my problem 1. However, it did not. I updated the repository with the Jackson annotations, but I cannot see any changes in the result.

=== Update 3 ===

Please ignore my Update 2. It totally works when using Jackson 2 instead of Jackson 1. Beginners mistake. :( Which leaves me with the question: Is it possible to get this working without touching the BaseEntity every time you add an entity.

like image 749
Martijn Burger Avatar asked Mar 29 '16 17:03

Martijn Burger


2 Answers

Instead of using JAXB for XML, you can use Jackson, which has an XML module. For JAX-RS you would use this artifact1

<dependency>
    <groupId>com.fasterxml.jackson.jaxrs</groupId>
    <artifactId>jackson-jaxrs-xml-provider</artifactId>
    <version>${jackson2.version}</version>

    <!-- you'll probably want to use the same version as
         the jackson being used on your wildfly -->

</dependency>

If you use this, the XML will be handle by Jackson, which doesn't have some of the same quirks that JAXB does. And all your Jackson JSON annotations will work with this also, so you only need one set of annotations, for both XML and JSON. At a lower level, the Jackson XML provider, uses jackson-dataformat-xml, if you want any more information about it.

From what I tested, just adding the artifact to your project is enough to make it work, though I did not test on Wildfly, I just tested with RESTeasy by itself. But I imagine is should still work.

If it doesn't work out the box, the only thing I could imagine is that the JAXB provider is taking precedence over this one. You may need to exclude the resteasy-jaxb-provider in a jboss-structure.xml file. But like I said, I don't think this will be required. I would've tested with Wildfly, but I really didn't feel like downloading it :-)

Extra

The OP is using classpath scanning2 to pick auto-register resources and providers, but if you are manually registering your resources and providers in your Application subsclass, you will also need to manually register the JacksonXMLProvider.class (or JacksonJaxbXMLProvider.class, if you want JAXB annotation support).


1 - The linked project shows as deprecated, but it links to the non-deprecated later version. I linked to the deprecated one, as it has some documentation in the README, though it is very little. The newer project has no documentation at all.

2 - Empty Application subclass annotated with @ApplicationPath is enough to trigger classpath scanning. Once you override either getClasses() or getSingletons() and return an non empty set, classpath scanning is disabled.

like image 130
Paul Samsotha Avatar answered Nov 09 '22 07:11

Paul Samsotha


All variables must be private in entity definition. If someone is public does return json response but not return xml response.

like image 36
Sedat Y Avatar answered Nov 09 '22 08:11

Sedat Y