20

I'm trying to write a generic function in Jersey which can be used to fetch a List of objects of the same type through REST. I based it on the informations found in this forum: link

@Override
public <T> List<T> fetchResourceAsList(String url) {
  ClientConfig cc = new DefaultClientConfig();
  Client c = Client.create(cc);
  if (userName!=null && password!=null) {
    c.addFilter(new HTTPBasicAuthFilter(userName, password)); 
  }
  WebResource resource = c.resource(url);
  return resource.get(new GenericType<List<T>>() {});
}

However this is not working. If i try to execute it, i get the following error: SEVERE: A message body reader for Java class java.util.List, and Java type java.util.List<T>, and MIME media type application/xml was not found.

However if i write this function without templating (replacing T with an actual class name) it just works fine. Of course this way the function loses it's meaning.

Is there a way to fix this?

NagyI
  • 5,907
  • 8
  • 55
  • 83
  • See below link http://stackoverflow.com/questions/1603404/using-jaxb-to-unmarshal-marshal-a-liststring – fmucar Aug 12 '11 at 09:28
  • @fmucar: this again has tips for the server as i see. I need generic type support for the client. – NagyI Aug 12 '11 at 11:28

3 Answers3

27

I've found solution https://java.net/projects/jersey/lists/users/archive/2011-08/message/37

public <T> List<T> getAll(final Class<T> clazz) {

    ParameterizedType parameterizedGenericType = new ParameterizedType() {
        public Type[] getActualTypeArguments() {
            return new Type[] { clazz };
        }

        public Type getRawType() {
            return List.class;
        }

        public Type getOwnerType() {
            return List.class;
        }
    };

    GenericType<List<T>> genericType = new GenericType<List<T>>(
            parameterizedGenericType) {
    };

    return service.path(Path.ROOT).path(clazz.getSimpleName())
            .accept(MediaType.APPLICATION_XML).get(genericType);
}
  • This worked form me with Jersey 2.9, thank you so much!! – Chris Hinshaw Jun 05 '14 at 15:44
  • BTW, Extended on your example and used the base class Collection.class for OwnerType and RawType and this worked also. So I should be able to reuse the single parameterizedType for all collection types. – Chris Hinshaw Jun 05 '14 at 15:48
6

See GenericType class of jersey which may help you as well

Unmarshaller needs to know what type the object there is before it can unmarhall the returned content . As generics information is not available at runtime so what you are asking is not possible. It cant unsmarhall something that it does not know anything about.

best you can do is ;

public <T> List<T> fetchResourceAsList(Class<?> beanClass, String url) {
    ...

   if(beanCLass.equals(MyBean.class)){
      return resource.get(new GenericType<List<MyBean>>()
   }else if(...){
      ...
   }...
}

or with generics warnings (I am not sure if this will work)

public List fetchResourceAsList(String url) {
    ...
      return resource.get(new GenericType<List<Serializable>>()
}
fmucar
  • 14,361
  • 2
  • 45
  • 50
  • You have provided a sample for the server side. How can this help me on the client side? I can view the list generated by Jersey server in the browser. My problem is that the client can't fetch the resource if i'm using generic Type in the above function. I can fetch the resource if i manually construct the client every time at place for a specific type. However i don't want this. – NagyI Aug 12 '11 at 11:11
  • Okay i got. It's the same problem described here: http://janmaterne.wordpress.com/2008/10/10/getting-the-type-of-a-generic-class-at-runtime/ – NagyI Aug 13 '11 at 17:01
  • May I ask why the generics information isn't available during runtime? – gosua Jul 29 '15 at 06:06
3

Extending on user2323189, you can use Collection instead of List.class so that you will be able to serialize all types extending Collection. I create a simple getter in my base client class and can simply use this to retrive any type of Collection. Not the clazz variable is supplied provided in my constructor so it is not a provided argument. You could probably turn this into a static method and use the signature GenericType> with a parameter for Class to make it even more generic.

public GenericType<Collection<T>> getParameterizedCollectionType() {
        ParameterizedType parameterizedGenericType = new ParameterizedType() {
            public Type[] getActualTypeArguments() {
                return new Type[] { clazz };
            }

            public Type getRawType() {
                return Collection.class;
            }

            public Type getOwnerType() {
                return Collection.class;
            }
        };
        return new GenericType<Collection<T>>(parameterizedGenericType){};
    }
Chris Hinshaw
  • 6,967
  • 2
  • 39
  • 65