Search code examples
javajax-rsresteasywildfly-9

@Produces collection in JAXRS / RestEasy


I found some strange behaviour that I cannot understand.

I have tested 4 similar examples:

1

@GET
@Produces(MediaType.APPLICATION_JSON)
public Response produce() {
    List<Book> books = Arrays
            .asList(new Book[] { 
                    new Book("aaa", "AAA", "12345"), 
                    new Book("bbb", "BBB", "09876") 
                    });
    return Response.ok(books).build();
}

2

@GET
@Produces(MediaType.APPLICATION_JSON)
public List<Book> produce() {
    List<Book> books = Arrays
            .asList(new Book[] { 
                    new Book("aaa", "AAA", "12345"), 
                    new Book("bbb", "BBB", "09876") 
                    });
    return books;
}

3

@GET
@Produces(MediaType.APPLICATION_XML)
public List<Book> produce() {
    List<Book> books = Arrays
            .asList(new Book[] { 
                    new Book("aaa", "AAA", "12345"), 
                    new Book("bbb", "BBB", "09876") 
                    });
    return books;
}

4

@GET
@Produces(MediaType.APPLICATION_XML)
public Response produce() {
    List<Book> books = Arrays
            .asList(new Book[] { 
                    new Book("aaa", "AAA", "12345"), 
                    new Book("bbb", "BBB", "09876") 
                    });
    return Response.ok(books).build();
}

Everything works in #1, #2, #3 but 4th example throws:

Could not find MessageBodyWriter for response object of type: java.util.Arrays$ArrayList of media type: application/xml.

I run it on Wildfly 9 and I wonder if it is related to RestEasy or JaxRS in general? I know that I can fix it by wrapping collection in GenericEntity, but I don't understand this inconsistent behaviour.


Solution

  • The problem is the missing of type information. This is required for JAXB, which handles the XML serialization.

    1 and 2 works because Jackson is being used for JSON, and it generally doesn't need to know type information as it just introspects properties.

    3 works because type information is known through the method return type.

    4 doesn't work because there is no type information. It's is erased by type erasure. That's where GenericEntity comes to the rescue. It stores the type information.

    GenericEntity

    Normally type erasure removes generic type information such that a Response instance that contains, e.g., an entity of type List<String> appears to contain a raw List<?> at runtime. When the generic type is required to select a suitable MessageBodyWriter, this class may be used to wrap the entity and capture its generic type.