What is the most portable (or standard) way of ret

2019-09-03 12:07发布

问题:

Well, anybody can say this question is already asked and answered. Never mind I also found them.

  • JAX-RS: How to automatically serialize a collection when returning a Response object?
  • Using Java-generics template type in RESTful Response object via GenericEntity<List<T>>

I'm not confused with the way of responding with an List<T> but just not convinced about the portability.

Let's say I have a nicely annotated entity looks like this.

@XmlRootElement
public class Stock {

    @XmlAttribute
    private Long id;
}


List<Stock>

When do like this,

@GET
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public List<Stock> readStocks() {
    final List<Stock> list = getStocks();
    return list;
}

As far as know, GlassFish and WileFly works.

<stocks> <!-- automatic plural? -->
  <stock xmlns="http://...">
  </stock>\
  <stock xmlns="http://...">
  </stock>
</stocks>

<collection> <!-- fixed? -->
  <stock xmlns="http://...">
  </stock>\
  <stock xmlns="http://...">
  </stock>
</collection>

and JSON, (it could be vary by providers, i think)

[
    {
        "id": 1
    },
    {
        "id": 2
    }
]


GenericEntity<List<Stock>>

Sometime I see a problem when the container couldn't find a MessageBodyWriter. So that I do like this.

@GET
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public GenericEntity<List<T>> readStocks() {
    return new GenericEntity<List<Stock>>(getStocks()) {};
}

or

@GET
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public Response readStocks() {
    return Response.ok(new GenericEntity<List<Stock>>(getStocks()) {}).build();
}


WrapperClass

@XmlRootElement
public class Stocks {
    @XmlElement
    private List<Stock> stock;
}

@GET
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public Stocks readStocks() {
    final Stocks entity; ///
    return Response.ok(entity).build();
}

According to 4.2.4 Standard Entity Providers (JSR-339), List is not on the list of mandatory prepackaged MessageBody(Reader|Writer).

Is List<T> standardized? or Which way is the most portable one?

回答1:

It's not really a problem with List. The MBWs for both JSON and XML generally always return true for isWritable. That's how they are able to handle all types you throw are them. What is has to do with is type erasure. The purpose of the GenericEntity is that is caches the generic type parameter, which allows the MBW to know what type to marshal as.

Note that GenericEntity is generally use when returning Response. Imagine this case

public Response get() {
    List<Somthing> l = service.get();
    return Response.ok(l).build();
}

Because of type erasure, there's just no way for MBW to know the type, by the time the entity gets to it. With some provider it doesn't matter. For example with Jackson it generally doesn't need to know the type, as it just introspects properties for serialization. But with MOXy/JAXB, it inherently needs to know the class. That's where the GenericEntity comes to play.

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.

...

List<String> list = new ArrayList<String>();
GenericEntity<List<String>> entity = new GenericEntity<List<String>>(list) {};
Response response = Response.ok(entity).build();