How to write an XML MessageBodyWriter provider with jersey
Typically one would use a MessageBodyWriter
to convert objects to data formats that Jersey knows nothing about. Here's an example of translating a TableData
domain object to CSV:
@Singleton
@Produces("text/csv")
@Provider
public class FederationCsvWriter implements MessageBodyWriter<TableData> {
@Override
public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
return TableData.class.isAssignableFrom(type);
}
@Override
public long getSize(TableData data, Class<?> type, Type genericType, Annotation annotations[], MediaType mediaType) {
return -1;
}
@Override
public void writeTo(TableData data, Class<?> type, Type genericType, Annotation[] annotations,
MediaType mediaType, MultivaluedMap<String, Object> headers, OutputStream out) throws IOException {
Writer osWriter = new OutputStreamWriter(out);
CSVWriter writer = new CSVWriter(osWriter, ',', '"', "\r\n");
if (data.getResultCount() > 0) {
//Write the header
writer.writeNext(data.getResult().get(0).keySet().toArray(new String[data.getResult().get(0).keySet().size()]));
//Write the data
for (ModelData row: data.getResult()) {
writer.writeNext(row.values().toArray(new String[row.values().size()]));
}
}
writer.flush();
}
}
In your example you could either create XML by inspecting the object yourself and feeding the result to the OutputStream
or your could use JAXB (which Jersey uses internally) to marshall the objects directly to the OutputStream
. For the purposes of your exercise JAXB is probably more interesting.
Leonardo
Updated on June 07, 2022Comments
-
Leonardo almost 2 years
I am not trying to solve any problem in particular, but rather I am on a learning path to jersey.
I have an entity class marked like this:
@Entity @Table(name = "myentity") @XmlRootElement public class MyEntity implements serializable { // lots of methods... }
and the corresponding jersey service
@Stateless @Path("entity") public class EntityFacade { @GET @Path("{param}") @Produces({"application/xml;charset=UTF-8"}) public List<MyEntity> find(@PathParam("param") String param) { List entities = entityManager.getResultList(); // retrieve list from db return entities; } }
Which give a correct XML response. Supposing I want to write a MessageBodyWriter which replicate same behavior, which is producing an XML response, how could I do that ?
@Provider public class TrasformerMessageBodyWriter implements MessageBodyWriter<Object> { @Override public long getSize(Object o, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) { return 0; } @Override public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) { // return true or false depending if I want to rewrite the response } @Override public void writeTo(Object o, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException { // what do I need to write here... } }
by marking with @Provider annotation I can see the message body writer is correctly invoked.
When writeTo is invoked, Object o is a Vector and Type genericType is a List but at this point I am completely lost on how I could transform the object in XML.
Last, if everything is already provided by jersey and its annotations, how can a MessageBodyWriter be useful ?
Again, I am repeating that this is just an academic exercise.
-
Leonardo almost 11 yearsUsing JAXB Unless I write a wrapper class, the List is returned by the web service as a Vector<MyEntity> therefore I cannot marshall it.
-
Stony almost 11 yearsCan you explain that getSize should return -1 ?
-
condit almost 11 years@StonyZhang - From the javadoc ( goo.gl/XyrGV ):
Called before writeTo to ascertain the length in bytes of the serialized form of t. A non-negative return value is used in a HTTP Content-Length header.
-
Stony almost 11 yearsThanks a lot for your clarification.
-
mdarwin about 9 yearsAccording to the spec you're not supposed to close the OutputStream. Will closing the writer not close the OutputStream too?
-
Nelda.techspiress almost 7 yearsThe -1 return value for getSize() is due to changes in JAX-RS and getSize() has been deprecated.
As of JAX-RS 2.0, the method has been deprecated and the value returned by the method is ignored by a JAX-RS runtime. All MessageBodyWriter implementations are advised to return -1 from the method. Responsibility to compute the actual Content-Length header value has been delegated to JAX-RS runtime.
Here is a new link to the above reference by @condit.