restlet / restlet-framework-java

The first REST API framework for Java
https://restlet.talend.com
Other
654 stars 284 forks source link

JSON deserialization and type erasure #556

Closed maxhille closed 9 years ago

maxhille commented 12 years ago

When retrieving JSON data and using the Jackson-Extension, it would be nice to be able to use some mechanism for working around Java's type erasure.

The following snipped shows how it would look like if we would use Jackson's method.

ClientResource resource = new ClientResource("http://localhost:8182/example");
List<Example> examples = resource.get(new TypeReference<List<Example>>() {});
Tembrel commented 12 years ago

Restlet core should not have a dependency on Jackson's TypeReference, so if this is to be addressed, it would probably have to involve a Restlet version of TypeReference.

Note that there are several reasonable workarounds: You can define a non-parameterized subtype, nested in the same class that calls ClientResource.get:

public static class ExampleList extends ArrayList<Example> { }
...
ClientResource resource = new ClientResource("http://localhost:8182/example");
ExampleList examples = resource.get(ExampleList.class);

Or you can use the annotation style:

// Common interface
public interface ExampleListResource {
    @Get List<Example> getExamples();
}

// Server impl:
public class ExampleListServerResource extends ServerResource implements ExampleListResource {
    @Override public List<Example> getExamples() { ... impl here ... }
}

// Client side call:

ClientResource resource = new ClientResource("http://localhost:8182");
ExampleListResource examplesResource = resource.getChild("/example", ExampleListResource.class);
List<Example> examples = examplesResource.getExamples();
cstrempfer commented 12 years ago

I am struggling with a similar problem because of type erasure on Android. I tried to use a wrapper, but apparently it doesn't work.

import org.restlet.engine.Engine;
import org.restlet.ext.jackson.JacksonConverter;
import org.restlet.resource.ClientResource;

public void getUsers() {
    Engine.getInstance().getRegisteredConverters()
            .add(new JacksonConverter());
    ClientResource cr = new ClientResource(URL);
    UserResource resource = cr.wrap(UserResource.class);
    List<User> users = resource.getUsers();

With that code I'm getting a List of LinkedHashMaps and that causes a ClassCastException. Unfortunately Jackson Annotations are ignored.

import org.codehaus.jackson.map.annotate.JsonDeserialize;
import org.restlet.resource.Get;

public interface UserResource {
    @Get
    @JsonDeserialize(contentAs = User.class)
    public List<User> getUsers();
}

That's because the converter never gets his hands on the interface.

I think it's a little unhandy, that in case of a wrapper ClientResource decides about the target class, instead of giving the converter a chance to extract informations from the wrapped interface. Especially since annotations are more commonly used in Java frameworks, there is a lot of configuring going on within interfaces.

@Tembrel: I don't see how your second example should solve the type erasure issue.

(P.S.: The Code examples are shortened.)

Tembrel commented 12 years ago

I think there's general agreement that it would be nice to find a way around the limitation on returning parameterized types from wrapped resources, but I suspect there's already an existing issue for this.

jgustie commented 11 years ago

I think that making the ConverterHelper accept java.lang.reflect.Type instead of java.lang.Class would be a good first step in the right direction: it would at least make the parameterized type information available to the converter.

maxhille commented 9 years ago

Just noticed this forever-open issue. Closing it because it has probably been superseeded by something else by now.

thboileau commented 9 years ago

linked to #924