Reputation: 12405
I have a generic ServiceResponse class as follows:
@XMLRootElement
public class ServiceResponse<T>
{
private T data;
private String error;
//setters n getters
}
From my RESTEasy Service, i want to generate xml response as:
List<Customer> customers = someDAO.getCustomers();
ServiceResponse<List<Customer>> resp = new ServiceResponse<List<Customer>>();
resp.setData(customers);
resp.setError("No Error");
return resp;
or return Response.ok().entity(resp).build();
But this is throwing error as there is no JaxbMarshallWriter for java.util.List.
I can marshall List usinig GenericEntity class.
GenericEntity<List<Customer>> entity = new GenericEntity<List<Customer>>(customers){};
Response.ok(entity).build();
But GenericEntity<ServiceResponse<List<Customer>>>
is not working saying no JaxbMarshallWriter for java.util.List.
Is there any work around to marshall/unmarshall classes with generic templates(, )?
Upvotes: 9
Views: 3313
Reputation: 5635
I know much late to reply but as there is no up voted answer I will try to give my answer hope it helps.
The problem is when you have a generic class say MyClass jaxB excepts T is annotated with either @XMLRootElement or @XMLType.
in your code scenario your type T is of List List does not have any of @XMLRootElement or @XMLType so it throws error. I think the solution for above case is create a wrapper class for Collection like
@XMLRootElement
Class JaxBCollection<T>{
java.util.Collection<T> collection;
/* Have getters and setters*/
}
now in your code have something like this.
List<Customer> customers = someDAO.getCustomers();
JaxBCollection<Customer> jaxBCustomers= new JaxBCollection<Customer>();
jaxBCustomers.setCollection(customers);
ServiceResponse<JaxBCollection<Customer>> resp = new ServiceResponse<JaxBCollection<Customer>>();
resp.setData(jaxBCustomers);
resp.setError("No Error");
return resp;
Upvotes: 0
Reputation: 1140
I'm not sure if it makes a difference that your class uses generic templates, but this is how I would generate an XML response using RESTEasy
This is the class that would hold your service response
public class ServiceResponse<T>
{
private T data;
private String error;
//setters n getters
}
This is the class that would actually transform your response into XML. This class really doesn't do much other than take in and produce XML/JSON or whatever you are using. It then passes the request on to the class that does the real work. This however is the class that would answer your specific question (I believe).
@Path("/myrestservice")
public class SomeRestService
{
private SomeCoreService coreService;
//getters and setters here
@POST
@Path("/examples/")
@Consumes({MediaType.APPLICATION_XML}) //this consumes XML
@Produces({MediaType.APPLICATION_XML}) //this produces XML
public ServiceResponse<T> exampleFunction(Request request)
{
try
{
//Unwrap the request and take only what you need out
//of the request object here
return this.coreService.examples(request.getStringFromRequest());
}
catch(Exception ex)
{
return new ServiceResponse<T>(Put response error message here);
}
}
}
This is the class that does all of the real work.
public class SomeCoreService
{
public ServiceResponse<T> examples(String stringFromRequest)
{
//do whatever work you need to do here.
return new ServiceResponse<T>(put whatever you need in the service response here)
}
}
Also, I haven't tested any of this. Hopefully it is enough for you to get the pattern.
Upvotes: 1
Reputation: 359
A solution that I had done for the same problem, was the creation of a new type to simulate the generic type List, as I had done, I created a new type that I named Container (for example : PersonContainer) in which there is a list of my entity (Person), that I use instead of the List type, and it works very well...
Here you have my example if it can be useful for you :
package com.dosideals.server.beans;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.xml.bind.annotation.XmlRootElement;
/**
*
* @author LOTFI
*/
@Entity
@XmlRootElement
public class Admin implements Serializable {
@Id
private String login;
private String password;
private String firstName;
private String lastName;
public Admin() {
}
public Admin(String login, String password, String firstName, String lastName) {
this.login = login;
this.password = password;
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getLogin() {
return login;
}
public void setLogin(String login) {
this.login = login;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Admin other = (Admin) obj;
if ((this.login == null) ? (other.login != null) : !this.login.equals(other.login)) {
return false;
}
return true;
}
@Override
public int hashCode() {
int hash = 7;
hash = 83 * hash + (this.login != null ? this.login.hashCode() : 0);
return hash;
}
@Override
public String toString() {
return "Admin{" + "login=" + login + ", password=" + password + ", firstName=" + firstName + ", lastName=" + lastName + '}';
}
}
And this is the container AdminContainer :
package com.dosideals.server.beans.containers;
import com.dosideals.server.beans.Admin;
import java.util.List;
import javax.xml.bind.annotation.XmlRootElement;
/**
*
* @author LOTFI
*/
@XmlRootElement
public class AdminContainer {
private List<Admin> admin;
public AdminContainer() {
}
public AdminContainer(List<Admin> admin) {
this.admin = admin;
}
public List<Admin> getAdmin() {
return admin;
}
public void setAdmin(List<Admin> admin) {
this.admin = admin;
}
}
Upvotes: 0
Reputation: 2786
The problem is not the generic the problem is that you should wrap your list inside an object.
ServiceResponse<ResponseData<Customer>> resp = new ServiceResponse<ResponseData<Customer>>();
You can then annotate ResponseData class to represent a set of objects.
Upvotes: 0