Description
When you request a collection from the WebClient it calls readFrom() using the type == Collection.class and the genericType == the member type of the class.
Unfortunately, this isn't what is normally passed as the genericType when reflection or a GenericEntity was used to write out an object. Normally, the genericType == Collection<X>. In order to be more compatible with third-party providers, it would be best if the generic type was set consistently in each case, rather than having a special case for the collections.
To support this, the genericType should be set to a subclass of ParameterizedType, for example an instance of this class:
import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.Collection; public final class ParameterizedCollectionType<T> implements ParameterizedType { private final Class<T> collectionMemberClass; private final Type[] typeArgs; public ParameterizedCollectionType(Class<T> collectionMemberClass) { this.collectionMemberClass=collectionMemberClass; this.typeArgs=new Type[] { collectionMemberClass }; } public Type[] getActualTypeArguments() { return typeArgs; } public Type getOwnerType() { return null; } public Type getRawType() { return Collection.class; } public String toString() { return "java.util.Collection<"+collectionMemberClass.getName()+">"; } }
If this class was used in org.apache.cxf.jaxrs.client.WebClient.invokeAndGetCollection(String, Object, Class<T>) then the client would be able to read a JSON collection response using the JacksonJsonProvider.
As a workaround I have created a subclass of JacksonJsonProvider that "fixes" this issue by overriding readFrom():
/** * For collections, CXF passes type == Collection.class and genericType == the member class type. * * But actually, it should pass genericType = Collection<X> as the generic type in order for Jackson * to understand it. */ @SuppressWarnings("unchecked") @Override public Object readFrom(Class<Object> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, String> httpHeaders, InputStream entityStream) throws IOException { if(type.equals(Collection.class) && !(genericType instanceof ParameterizedType)) { genericType = new ParameterizedCollectionType<Object>((Class)genericType); } return super.readFrom(type, genericType, annotations, mediaType, httpHeaders, entityStream); }