Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to write an XML MessageBodyWriter provider with jersey

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.

like image 234
Leonardo Avatar asked May 24 '13 18:05

Leonardo


1 Answers

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.

like image 158
condit Avatar answered Sep 28 '22 01:09

condit