Jersey JAX-RS Client XML to java.util.List deserialization - java

I am trying to access a JAX-RS Service (jersey implementation) which is returning me a java.util.list of Employees in XML format.
The Service method signature looks like this:
#GET
#Path("/getEmployeeListXML")
#Consumes(MediaType.TEXT_PLAIN)
#Produces(MediaType.APPLICATION_XML)
public List<EmployeeXML> getEmployeeListXML(#QueryParam("id") String id){
//Some code which returns a List<EmployeeXML>
}
The format of the XML returned is like this:
<employeeXMLs>
<employeeXML>
<empId>1</empId>
<empName>John</empName>
<empAge>35</empAge>
<empSex>Male</empSex>
</employeeXML>
<employeeXML>
<empId>2</empId>
<empName>Lisa</empName>
<empAge>23</empAge>
<empSex>Female</empSex>
</employeeXML>
</employeeXMLs>
For accessing this from my jersey Client, I am using this code:
List<EmployeeXML> empListXML = (List<EmployeeXML>)service.path("rest").path("GetService").path("getEmployeeListXML").accept(MediaType.APPLICATION_XML).get(EmployeeXML.class);
This is not correct since the return type should be a list but in the get method, presently I am trying to retrieve a single object. I am not sure how to retrieve the List from the client here :(
I am getting this exception:
unexpected element (uri:"", local:"employeeXMLs"). Expected elements are <{}employeeListXML>,<{}employeeXML>
Please help me out to make this work.
Thanks,

You can use the GenericType class to fetch a list of objects:
List<EmployeeXML> empListXML = (List<EmployeeXML>)service.path("rest").path("GetService").path("getEmployeeListXML").accept(MediaType.APPLICATION_XML).get(new GenericType<List<EmployeeXML>>(){});

You need to use a 'supertype token' to define the return type in your client class:
List<EmployeeXML> empListXML = service
.path("rest")
.path("GetService")
.path("getEmployeeListXML")
.accept(MediaType.APPLICATION_XML)
.get(new GenericType<List<EmployeeXML>>() {});
The supertype token is required in order to 'retain' generic parameter information that Jersey will use when deserializing the server response.

Related

Returning list of values in Restful webservices

I am working on a small example where I want to return list of values from my webservice:
#GET
#Path("/string")
#Produces({"application/xml", "application/json"})
public List<String> test2()
{
List<String> list = new ArrayList<String>();
list.add("USA");
list.add("EGYPT");
return list;
}
I am able to get response by calling this service successfully.
List list1 = client.target("http://localhost:8080/services/customers/string")
.request()
.accept(MediaType.APPLICATION_JSON)
.get(List.class);
It gives me data as [USA, EGYPT]
But it seems we need to use GenericEntity as per this post - Jersey: Return a list of strings
or as a wrapper class - How do return Java List<String> Json using Jax-RS
When we need to use these 2 options? Is it required to use GenericEntity or Wrapper classes?
I am using JAX-RS2.0 with Jersey implementation.
The serialization of your response is done by a jersey entity provider according to a specific algorithm. You should have a default provider in the context of your application supporting the serialization of your instance of List. Thus you do not need further wrapping.

Inserting annotations into Retrofit request for Jackson

I'm using Retrofit 2.1 and Jackson in an Android app to make a post call to a service which is using Jackson to deserialize. The service is expecting the incoming json to look like:
{"#class":"com.servicepackagepath$OurClassName","prop1":"value1","prop2":"value2"}
however the json that's getting generated and being sent to the service is:
{"prop1":"value1","prop2":"value2"}
resulting in a 500. The retrofit signature looks like:
#POST("apiName")
Call<String> apiName(#Body OurClassName ourClassName);
OurClassName class on the client side is a different class than the one on the service. Exactly the same signature, but it's local.
How can I get that #class annotation to get inserted into the outgoing json?
Ok, figured it out. You need to add to your Retrofit Builder:
.addConverterFactory(JacksonConverterFactory.create(
new ObjectMapper().enableDefaultTyping(ObjectMapper.DefaultTyping.JAVA_LANG_OBJECT, JsonTypeInfo.As.PROPERTY)
))
and then on top of your class declaration do:
#JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include= JsonTypeInfo.As.PROPERTY, property="#class")

Jersey, JSR 303 validation - custom "path" and "invalidValue" in ValidationError

I am using Jersey (JAX-RS) and I'm trying to implement a validation. I have a problem with a response returned by my application when a validation error occurs. Now the response looks like this:
[{
"message": "Custom message",
"messageTemplate": "{custom.message.template}",
"path": "SomeJerseyResource.resourceMethod.arg0.names[0]",
"invalidValue":"[value1, value2]"
}]
where "SomeJerseyResourceClass.resourceMethod" is a JAX-RS resource:
public class SomeJerseyResource {
#POST
#Path("/path")
public Response resourceMethod(#Valid RequestModel request) {
/** method body **/
}
}
and validation constraint is assigned to a getter in RequestModel:
public class RequestModel {
private List<String> names = new ArrayList<>();
#MyConstraint
public List<String> getNames() {
return tags;
}
}
I have a custom ConstraintValidator, where I validate each element of that List.
Problem
I don't want to include resource and method name in "path" field of the response. Instead of
SomeJerseyResource.resourceMethod.arg0.names[0] I want arg0.names[0] only. Client doesn't know about server classes and methods, and he wouldn't be able to properly assign errors to fields when he receives response like that.
I want to customize "invalidValue" field of a response. More specifically, to have only invalid element value, not the whole list in that field.
I didn't find any easy way to do that. Do you have any ideas?
You can just write an ExceptionMapper<ConstraintViolationException> to return the Response of your liking. Jersey uses an ExceptionMapper<ViolationException>. ConstraintViolationException extends from ViolationException, so you're mapper is more specific, and would take precedence in the choosing of the mapper. Jersey's mapper, returns the response as a ValidationError, that's why the body is how it is. But you can make it whatever you want.
If you just want the invalidValue list, then just iterate through the ConstraintViolations from ContraintViolationException.getConstraintViolations(), and get the invalidValue from the ConstraintViolation.

RestEasy - Jax-rs - Sending custom Object in response body

How do I send my custom object in a response. I just want the values printed from my object.
Lets say I have an object of type Person. I am trying to send in REST response body like this.
ResponseBuilder response = Response.ok().entity(personObj);
return response.build();
But I get 500 error.
Tried this one too:
ResponseBuilder response = Response.status(Status.OK).entity(personObj);
return response.build();
Same error.
Tried setting content type as text/xml. No use.
What am I missing here? I tried googling. But not many examples out there, especially with the custom objects;
It returns fine, if I just pass a string to entity() method.
In order to return data from a Resteasy resource method you need to do several things depending on what you are trying to return.
You need to annotate your resource method with the #Produces
annotation to tell Resteasy what the return type of the method should
be.
For example, the method below returns XML and JSON depending on what the client asks for in their Accept header.
#GET
#Produces({MediaType.APPLICATION_JSON,
MediaType.APPLICATION_XML})
public Response foo()
{
PersonObj obj = new PersonObj();
//Do something...
return Response.ok().entity(obj).build();
}
Resteasy supports marshalling the following datatypes by default:
If the datatypes you wish to support are in this table then that
means they are supported by JAXB and all you need to do is annotate
your PersonObj class with JAXB annotations to tell it how to
marshall and unmarshall the object.
#XmlRootElement
#XmlType(propOrder = {"firstName", "lastName"})
public class PersonObj
{
private String firstName;
private String lastName;
//Getters and Setters Removed For Brevity
}
What if your content-type is not supported out of the box?
If you have a custom content-type that you would like to marshall then you need to create a MessageBodyWriter implementation that will tell Resteasy how to marshall the type.
Provider
#Produces({"application/x-mycustomtype"})
public class MyCustomTypeMessageBodyWriter implements MessageBodyWriter {
}
Just implement the interface and register it like any other Provider.
If you would like to read a custom content-type then you need to implement a custom MessageBodyReader to handle the incoming type and add it to the #Consumes annotation on your receiving method.

RESTEasy client: reconstructing an object

I'm playing with RESTEasy to consume REST services, and I'm trying it out with Twitter's search API.
So I create this interface:
public interface SimpleClient {
#GET
#Path("search.json")
#Produces("application/json")
ClientResponse<Set<String>> getSearchResults(
#QueryParam("q") String hashtag,
#QueryParam("result_type") String resultType
);
}
and called it with:
SimpleClient client =
ProxyFactory.create(SimpleClient.class,"http://search.twitter.com/");
ClientResponse<Set<String>> response =
client.getSearchResults("#wowodc","recent");
System.out.println(response.getEntity(Set.class));
But I'm getting:
ClientResponseFailure: Unable to find a MessageBodyReader of content-type application/json;charset="utf-8" and type interface java.util.Set
I have tried using a POJO instead of java.util.Set, but I'm getting the same kind of exception. The only thing that didn't throw an exception is using String instead of Set.
By reading some example code on the Web, I was thinking that Set or a POJO as the entity type would have work, but it doesn't for me. The query to Twitter did return valid results.
You need to make sure you include a RESTEasy provider that can unmarshal JSON responses. There's a one based on the Jackson parser library that you can use, it's described in the docs here.

Categories

Resources