@Provider
@Produces(MediaType.TEXT_HTML)
public class UsersProvider implements MessageBodyWriter{
@Override
public void writeTo(Users users, Class type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMapparams, OutputStream os) throws IOException, WebApplicationException {
try {
JAXBContext jc = new JSONJAXBContext(JSONConfiguration.natural().build(), new Class[]{users.getClass()});//JAXBContext.newInstance(users.getClass());
DOMResult xml = new DOMResult();
jc.createMarshaller().marshal(users, xml);
StreamSource styleSheet = new StreamSource(getClass().getResourceAsStream("/stylesheet/Users.xsl"));
Transformer transformer = TransformerFactory.newInstance().newTransformer(styleSheet);
transformer.transform(new DOMSource(xml.getNode()), new StreamResult(os));
}catch (Exception ex) {
throw new RuntimeException(ex.getMessage(), ex);
}
}
@Override
public boolean isWriteable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) {
return (Users.class.isAssignableFrom(type) && mediaType.isCompatible(MediaType.TEXT_HTML_TYPE));
}
@Override
public long getSize(Users arg0, Class arg1, Type arg2, Annotation[] arg3, MediaType arg4) {
return -1;
}
}
In the end it's actually quite easy, but what threw me off was that JAX-RS has built-in support for the serialization of List<t> where T is a JAXB annotated class. The problem is that my custom provider, which applies a stylesheet to the XSL necessarily bypasses the built-in support for collections of JAXB annotated classes. Therefore, the method in my resource can't return a List
<users>
<user>...</user>
<user>...</user>
<!-- etc -->
</users>
versus naming the accessor method in the plural produces (note redundant plural users):
<users>
<user&sgt;...</users>
<users>...</users>
<!-- etc -->
</users>
so here is the JAXB annotated "wrapper" class for use instead of returning a raw List
@XmlRootElement(name = "users")
public class Users {
private List
/**
* @return the users
*/
@XmlElement
public List
return users;
}
/**
* @param users the users to set
*/
public void setUsers(List
this.users = users;
}
}
then the Resource method just looks like this (note that it returns the Users wrapper class):
@GET
@Produces({MediaType.TEXT_HTML})
@Path("/users/{user}")
public Users getUsersHTML(@PathParam("user") String username) throws Exception {
Users users = new Users();
if(username.equalsIgnoreCase("*")){
users.setUsers(net.nextdb.SysAdminSearchAccounts.getUsers("%"));
}else{
users.setUsers(net.nextdb.SysAdminSearchAccounts.getUsers(username));
}
return users;
}
Nice. I spent an how trying to figure out myself, and then another 1/2 hour trying to find someone else who knew. Your post demonstrates the ease of the implementation where jax-rs implementation isn't so easy to figure out =).
ReplyDeleteThanks.