问题
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:
- How to return a
JSON
response including the attributes for the specific object that is requested? - I would prefer it if the
BaseEntity
does not have to be touched when adding or removing specificEntity
classes. Because the@XmlSeeAlso
annotation every time I add a newEntity
class, I need to update the annotation. Is there another method to implement this where I do not need to touch theBaseEntity
?
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.
回答1:
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.
回答2:
All variables must be private in entity definition. If someone is public does return json response but not return xml response.
回答3:
Okay. In the end I came up with a seperate solution for XML and for JSON. In this solution I do not need any annotations on the Generic classes (Like @XmlSeeAlso
and @JsonSubTypes
). The only drawback to this solution is that you cannot return a Response
object in the Rest class for an XML request.
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getAllJSON() {
return Response.ok(getService().findAll()).build();
}
@GET
@Produces(MediaType.APPLICATION_XML)
public List<T> getAllXML() {
return getService().findAll();
}
If someone knows a better implementation, I'll delete this answer.
来源:https://stackoverflow.com/questions/36291250/jax-rs-response-for-xml-or-json-is-not-working