I would like to use a dynamically generated bean as the output for a Jersey REST WS. I don't know at compile time what properties will be requested. I can return a bean containing a list of fields, but I wanted to try returning a bean with dynamically created properties as the output would be a bit nicer.
I'm trying to use cglib to build a bean instance and return it to the Jersey framework to marshall into JSON/XML. I ran into the issue below, if anyone can make a suggestion I'd appreciate it. I don't know if I can use the magic of generics or create a custom handler to solve.
Libraries used: Tomcat 6.0.18, Java 1.6.0_17, Jersey v1.8, cglib v2.2
SearchWS
@Path("/search")
public class SearchWS
{
private static final Logger log = Logger.getLogger(SearchWS.class.getName());
@POST
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public GenericObject getXML(@Context final HttpServletRequest request, @FormParam("query") final String query)
throws Exception
{
log.debug("Entering");
final IDAO dao = new DAOImpl();
return dao.search(query).getResults().get(0);
}
}
GenericObject bean class
@XmlRootElement(name = "object")
@XmlAccessorType(XmlAccessType.PROPERTY)
public class GenericObject
{
}
DAO creates map of key/value pairs from the query and builds a GenericObject subclass instance
public GenericObject buildGenericBean(final Map<String, String> propertyValues) throws Exception
{
log.debug("Entering");
BeanGenerator bg = new BeanGenerator();
bg.setSuperclass(GenericObject.class);
bg.setNamingPolicy(new net.sf.cglib.core.NamingPolicy()
{
@Override
public String getClassName(final String prefix, final String source, final Object key, final Predicate names)
{
return prefix + "Impl";
}
});
Set<String> keys = propertyValues.keySet();
for (String nextKey : keys)
{
bg.addProperty(nextKey, String.class);
}
GenericObject bean = (GenericObject) bg.create();
for (String nextKey : keys)
{
setProperty(bean, nextKey, propertyValues.get(nextKey));
}
log.debug("Leaving");
return bean;
}
When the client requests JSON content ("application/json"), it produces some nice output as expected, based on the SQL query submitted as a form parameter:
{
"country": "DZ",
"title": "foo",
"theme": "foo",
"subject": "foo",
"owner_name": "foo",
"creation_date": "16/05/2013 06:20:10 PM",
"publication_needed": "1",
"language_code": "en",
"modify_date": "21/05/2013 05:08:15 PM",
"classification": "0"
}
Setting the requested content-type to XML ("application/xml") results in the error below. I had hoped that Jersey would ensure that the output would be equivalent e.g. elements named for the properties with text content as the property values. I'm not expert on XML marshalling though, so something could be missing. How can I get the equivalent output in XML?
Caused by: com.sun.jersey.api.MessageException: A message body writer for Java class xxx.model.GenericObjectImpl, and Java type class xxx.model.GenericObjectImpl, and MIME media type application/xml was not found