Jersey vs RestEasy - JAX-RS XML collection root element name change - java

I have the following Jax-RS end-point:
#XmlRootElement(name = "foobar")
public class Foobar {}
#GET
#Produces(MediaType.APPLICATION_XML)
public Object getFoobars() {
return new GenericEntity<List<FooBar>>(service.getFooBars());
}
Using Jersey 1.x, it used to return:
<foobars>
<foobar>...</foobar>
<foobar>...</foobar>
</foobars>
Now that I use RestEasy, it returns:
<collection>
<foobar>...</foobar>
<foobar>...</foobar>
</collection>
How can I control the root name of a returned GenericEntity<List<X>> in Jax-RS (using Rest-Easy)?
Please note that I also return Json format and I need to keep the API backward-compatible (for exemple the root element is an array in Json and should stay the same)

Found the solution myself after digging a bit in the RestEasy source code. You just have to add the #Wrapped(element="___") annotation to the method:
import org.jboss.resteasy.annotations.providers.jaxb.Wrapped;
#GET
#Produces(MediaType.APPLICATION_XML)
#Wrapped(element = "foobars")
public Object getFoobars() {
return new GenericEntity<List<FooBar>>(service.getFooBars());
}
Works correctly for XML and properly ignored for JSON.

Related

Springfox / Swagger does not resolve polymorphic field

I'm having a simple Spring Boot application with one REST endpoint to return a "Job" object, which contains a list of polymorphics, next to other stuff.
We go Code First approach and try to create the API models to fit our needs. But the generated Api Doc does not represent our model the in it's full complexity, as it does not resolve the list of polymorphics.
The Job object looks like
#Data // Lombok Getters and Setters
public final class Job {
private String foo;
private String bar;
private List<Condition> conditionList;
}
Condition is a parent object for a set of different conditions
public abstract class Condition {
}
Two example implementations of a Condition would be
#Data
public final class Internal extends Condition {
private String nodeId;
}
and
#Data
public final class Timed extends Condition {
private ZonedDateTime timestamp;
}
The REST controller is stupidly simple:
#RestController
#RequestMapping("/hello")
public class MyController {
#GetMapping
public ResponseEntity<Job> getJob() {
return new ResponseEntity<>(new Job(), HttpStatus.OK);
}
}
Now, when I open the Swagger UI and look at the generated definition, the element conditionList is an empty object {}
I tried to use the #JsonSubTypes and #ApiModel on the classed, but there was no difference in the output. I might not have used them correctly, or maybe Swagger is just not able to fulfill the job, or maybe I'm just blind or stupid.
How can I get Swagger to include the Subtypes into the generated api doc?
We "fixed" the problem by changing the structure. So it's more of a workaround.
Instead of using a List of polymorphics, we now use a "container" class, which contains each type as it's own type.
The Condition object became a "container" or "manager" class, instead of a List.
In the Job class, the field is now defined as:
private Condition condition;
The Condition class itself is now
public final class Condition{
private List<Internal> internalConditions;
// etc...
}
And, as example, the Internal lost it's parent type and is now just
public final class Internal{
// Logic...
}
The Swagger generated JSON now looks like this (excerpt):
"Job": {
"Condition": {
"Internal": {
}
"External": {
}
//etc...
}
}
Useful display of polymorphic responses in Swagger UI with Springfox 2.9.2 seems hard (impossible?). Workaround feels reasonable.
OpenAPI 3.0 appears to improve support for polymorphism. To achieve your original goal, I would either
Wait for Springfox to get Open API 3.0 support (issue 2022 in Springfox Github). Unfortunately, the issue has been open since Sept 2017 and there is no indication of Open API 3.0 support being added soon (in Aug 2019).
Change to Spring REST Docs, perhaps adding the restdocs-api-spec extension to generate Open API 3.0.
We have run into similar problems with polymorphism but have not yet attempted to implement a solution based on Spring REST Docs + restdocs-api-spec.

How to access json Post method values in netbeans using Java

I'm struggling with my restful webservice (Java & Netbeans 8.2)
My method looks like:
#POST
#Path("/usedPacking")
#Consumes(MediaType.APPLICATION_JSON)
public void setUsedPackage( ??? ) {
???
}
Actually I would like to receive a json-message as post-data like:
{"PackageID":"12345","Used":"false"}
My question is:
What do I have to replace the "???" with?
For GET-Methods it is:
#QueryParam("ID") String input
Which allows me to access the variable specified as ID by using input.
Everything I've found so far didn't quite address the problem I face..
For a JAXRS webservice you can create an annotated class that maps to your json e.g.
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Package {
private String packageID;
private Boolean used;
// getters and setters here
}
then ??? will be your class
public void setUsedPackage(Package package)
When you post your json you'll need to specify the Content-type header as application/json
Here's a jaxrs tutorial I found that may help
http://www.logicbig.com/tutorials/java-ee-tutorial/jax-rs/post-example/

Enunciate with Swagger - cannot generate documentation resource models when using generics

I'm facing similar issue than the one described in that thread :
Can Enunciate generate docs for an API that handles generic types?
I am using enunciate 1.28 with spring and swagger modules enabled.
So considering an abstract resource class like :
public abstract class AbstractResource<T> {
#Autowired
private SomeService<T> service;
#Path("/{id}")
#GET
public T get(#PathParam("id") String id) {
return service.get(id);
}
#POST
public Response post(T entity) {
return service.post(entity);
}
}
and one concrete implementation :
#Path("/authors")
public class AuthorResource extends AbstractResource<Author> { }
Enunciate docs are not generated with the proper "Author" Data Model for both GET and POST methods.
For GET I have :
Response Body element: (custom)`
and POST :
Request Body element: entity`
For Swagger the Author model is not showing as the JSON model for GET as "responseClass" and POST for the body "dataType". Instead I get string for both.
However the Author model is listed in the AuthorResource.json generated in swagger/ui directory. The responseClass and dataType fields are just missing the link to the model.
Manually replacing :
"responseClass" : "string"` by `"responseClass" : "ns0_Author" (GET)
"dataType" : "string"` by `"dataType" : "ns0_Author" (POST)
does the trick.
Note : I confirm that on my side Author is annotated with #XmlRootElement and the Author class is included in my <api-import pattern="com.my.package.**"/> which is located in a jar file on the classpath.
Any ideas on how to tweak the Enunciate/Swagger documentation generation in that case ?
Thanks
Smells like a bug. Tracking it here.

RESTEasy can't find Message Body Writer for application/xml when using dynamically created class

Could not find MessageBodyWriter for response object of type: java.util.ArrayList of media type: application/xml
I am getting the above error when trying to return a response in xml of a list of dynamically created classes/dtos.
#GET
#Path("objects")
public Response getObjects(
#DefaultValue("json") #QueryParam("format") String format)
{
GenericEntity entity;
//I use cglib here to dynamically at runtime create a class called objectDto.
//The class is just a POJO.
List<Object> objectsDto = generateObjects(fields);
entity = new GenericEntity<List<Object>>(objectsDto){};
Response.ResponseBuilder rBuild;
if (format.equals("xml"))
{
rBuild = Response.ok(entity, MediaType.APPLICATION_XML);
}
else
{
rBuild = Response.ok(entity, MediaType.APPLICATION_JSON);
}
return rBuild.build();
}
The weird thing is I can return JSON representations of this object but not XML. Also I can return XML representations of not dynamically created classes.
I have the correct dependency in my Maven project for resteasy-jaxb-provider:
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxb-provider</artifactId>
<version>3.0.6.Final</version>
</dependency>
This is not possible using cglib out of the box. Since the XmlRootElement is not inherited, the subclass that is created by cglib will not longer carry this annotation. Cglib itself was written before annotations were introduced to Java and no recent update added this functionality. You can instead register an ASM visitor with the cglib enhancer which should be responsible for adding the annotation to the cglib generated class.
However, you might want to consider creating your classes using javassist which has a more modern API and supports the writing of annotations.

JAX-RS NoMessageBodyWriterFoundFailure

the method
of my jax-rs application:
#GET
#Produces (MediaType.APPLICATION_JSON)
public List <Document> getDocumentList(#HeaderParam("Range") String headerRange) {
int [] range = getRangeFromHeader(headerRange);
return facade.listByRange(range);
}
working properly.
But If modifications to the:
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response getDocumentList(#HeaderParam("Range") String headerRange) {
int[] range = getRangeFromHeader(headerRange);
return Response.ok(
facade.listByRange(range))
.header("Content-Range", getContentRangeStr(range)).build();
}
I receive an error
...NoMessageBodyWriterFoundFailure: Could not find MessageBodyWriter for response
object of type: java.util.ArrayList of media type: application/json...
Server Jboss 7.1.1
Please tell me what's wrong.
PS.sorry for my bad English.
The snippet below should do the trick.
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response getDocumentList(#HeaderParam("Range") String headerRange) {
int[] range = getRangeFromHeader(headerRange);
return Response.ok(
new GenericEntity<List<Document>>( (List<Document>)facade.listByRange(range))
)
.header("Content-Range", getContentRangeStr(range)).build();
}
The anonymous GenericEntity subclass is required to supply the correct type information (otherwise erased by the compiler) for the writer.
-- EDIT
The reason why your code worked using org.jboss.resteasy.resteasy-jackson-provider but not with org.jboss.resteasy.resteasy-jettison-provider resides on the fundamental difference between the two providers:
the former (jackson) relies on a JavaBean model, discovering the properties of the objects to serialize, and needs no type information
the latter (jettyson) relies on the JAXB annotations, so it needs the underlying type information, erased by the compiler.
You're missing a library as described here:
Here is the solution
This means that you are missing a JSON library in your classpath. Jackson is one I’m using so adding this to your pom.xml will help:
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.10.1</version>
</dependency>

Categories

Resources