Monday, May 27, 2013

RESTEasy: Erro ao retornar List em XML - Could not find MessageBodyWriter for response object of type: xxx application/xml

Outro dia me deparei com uma exception gerada pelo RESTEasy, o erro era: Could not find MessageBodyWriter for response object of type: java.util.ArrayList of media type: application/xml.

No meu cenário, o problema ocorria ao utilizar o componente Response do RESTEasy como retorno do método. Pra ficar mais claro a seguir um exemplo de código simulando o erro, no caso a entidade e o endpoint do RESTEasy:
@Entity
@XmlRootElement
public class Carro {

  @Id
  private Long id;

  @NotNull
  private String renavam;

  private String placa;

  @NotNull
  @ManyToOne
  private Modelo modelo;

  //...

}


@Path("/carros")
public class CarroEndpoint {
  ...

  @GET
  @Path("/{id:[0-9][0-9]*}")
  @Produces("application/xml")
  public Response findById(@PathParam("id") Long id) {
    try {
      Carro c = service.findById(id); 
      return Response.ok(c).build();
    } catch (NoResultException nrex) {
      return Response.status(Status.NOT_FOUND).build(); //404
    }
  }

  @GET
  @Produces("application/xml")
  public List<Carro> listAll() {
    return service.listAll();
  }

  @GET
  @Path("/{modelo}")
  @Produces("application/xml")
  public Response listByModelo(@PathParam("modelo") String descModelo) {
    Modelo m = service.findModeloByDescricao(descModelo);
    if (m == null) {
      return Response.status(Status.NOT_FOUND).build();
    }

    List<Carro> carros = service.findByModelo(m);
    return Response.ok(carros).build(); //o problema ocorre aqui
  }

}

Os  métodos findById e listAll funcionam corretamente, ambos devolvem o XML representando os dados dos carros encontrados de acordo com a consulta. Note o retorno desses dois métodos: a entidade Carro e uma lista de Carros.

A exception ocorre no terceiro método, no listByModelo, aonde faço uso da Response (componente do RESTEasy). Nesse caso o RESTEasy não consegue transformar o ArrayList em XML. A mesma situação acontece com outra coleção, por exemplo HashSet.

Pra resolver o problema, eu utilizei o GenericEntity para "tipar" a lista, de forma que o RESTEasy possa realizar a transformação em XML. A mesma abordagem funciona com HashSet. A seguir a nova versão de listByModelo:
  ...

  @GET
  @Path("/{modelo}")
  @Produces("application/xml")
  public Response listByModelo(@PathParam("modelo") String descModelo) {
    Modelo m = service.findModeloByDescricao(descModelo);
    if (m == null) {
      return Response.status(Status.NOT_FOUND).build();
    }

    List<Carro> carros = service.findByModelo(m);
    GenericEntity<List<Carro>> entity = new GenericEntity<List<Carro>>(carros);
    return Response.ok(entity).build(); //ok
  }
  ...

A versão do RESTEasy utilizada foi a 2.3.6.Final.

[]s
http://twitter.com/edermag
http://www.yaw.com.br

No comments: