NBW
NBW

Reputation: 1487

JAX-RS Jersey Client marshaling JSON response with POJO MAPPING & Jackson

I'm having a bit of an issue using Jersey client (1.11) with JSONConfiguration.FEATURE_POJO_MAPPING set to true. My test code looks like this:

MyFooCollectionWrapper<MyFooDTO> resp
    = webResource.accept(MediaType.APPLICATION_JSON)
      .get(new GenericType<MyFooCollectionWrapper<MyFooDTO>>() {});

On the server:

1) my web.xml has POJO Mapping set to true.

2) MyFooDTO is simply a POJO that looks like this:

public class MyFooDTO {

private long id;
private String propA;

pubic long getId() {
    return id;
}
public void setId(long id) {
    this.id = id;
}

pubic String getPropA() {
    return propA;
}
public void setPropA(String propA) {
    this.propA = propA;
}

public MyFooDTO(MyFoo aFoo) {
    this.id = aFoo.getId();
    this.propA = aFoo.getPropA();
}

    public MyFooDTO() {}

}

3) MyFooCollectionWrapper looks like this:

public class MyFooCollectionWrapper<T> extends MyFooCollectionWrapperBase {

    Collection<T> aCollection;

    public MyFooCollectionWrapper() {
        super();
    }

    public MyFooCollectionWrapper(boolean isOK, String msg, Collection<T> col) {
        super(isOK, msg);
        this.aCollection = col;
    }

    public void setCollection(Collection<T> collection) {
        this.aCollection = collection;
    }

    @JsonProperty("values")
    public Collection<T> getCollection() {
        return aCollection;
    }
}

public class MyFooCollectionWrapperBase {

    private boolean isOK;
    private String message;

    public MyFooCollectionWrapperBase() {
        this.message = "";
        this.isOK = false;
    }

    public MyFooCollectionWrapperBase(boolean ok, String msg) {
        this.isOK = ok;
        this.message = msg;
    }

    .. standard getter/setters ..

}

I've verified server has no problem creating the Json response. I can retrieve with my Jersey client code if I set the response type to String. When I use

MyFooCollectionWrapper<MyFooDTO> resp = webResource.accept(MediaType.APPLICATION_JSON).get(new GenericType<MyFooCollectionWrapper<MyFooDTO>>() {});

I would expect POJO mapping to just work (marshall the response) without any need for a custom message body reader. However, I get:

Jun 04, 2012 3:02:20 PM com.sun.jersey.api.client.ClientResponse getEntity
SEVERE: A message body reader for Java class com.foo.MyFooCollectionWrapper, and Java type     com.foo. MyFooCollectionWrapper<com.foo.MyFooDTO>, and MIME media type application/json was not found
Jun 04, 2012 3:02:20 PM com.sun.jersey.api.client.ClientResponse getEntity
SEVERE: The registered message body readers compatible with the MIME media type are:
application/json ->
com.sun.jersey.json.impl.provider.entity.JSONJAXBElementProvider$App
com.sun.jersey.json.impl.provider.entity.JSONArrayProvider$App
com.sun.jersey.json.impl.provider.entity.JSONObjectProvider$App
com.sun.jersey.json.impl.provider.entity.JSONRootElementProvider$App
com.sun.jersey.json.impl.provider.entity.JSONListElementProvider$App
*/* ->
com.sun.jersey.core.impl.provider.entity.FormProvider
com.sun.jersey.core.impl.provider.entity.MimeMultipartProvider
com.sun.jersey.core.impl.provider.entity.StringProvider
com.sun.jersey.core.impl.provider.entity.ByteArrayProvider
com.sun.jersey.core.impl.provider.entity.FileProvider
com.sun.jersey.core.impl.provider.entity.InputStreamProvider
com.sun.jersey.core.impl.provider.entity.DataSourceProvider
com.sun.jersey.core.impl.provider.entity.XMLJAXBElementProvider$General
com.sun.jersey.core.impl.provider.entity.ReaderProvider
com.sun.jersey.core.impl.provider.entity.DocumentProvider
com.sun.jersey.core.impl.provider.entity.SourceProvider$StreamSourceReader
com.sun.jersey.core.impl.provider.entity.SourceProvider$SAXSourceReader
com.sun.jersey.core.impl.provider.entity.SourceProvider$DOMSourceReader
com.sun.jersey.json.impl.provider.entity.JSONJAXBElementProvider$General
com.sun.jersey.json.impl.provider.entity.JSONArrayProvider$General
com.sun.jersey.json.impl.provider.entity.JSONObjectProvider$General
com.sun.jersey.core.impl.provider.entity.XMLRootElementProvider$General
com.sun.jersey.core.impl.provider.entity.XMLListElementProvider$General
com.sun.jersey.core.impl.provider.entity.XMLRootObjectProvider$General
com.sun.jersey.core.impl.provider.entity.EntityHolderReader
com.sun.jersey.json.impl.provider.entity.JSONRootElementProvider$General
com.sun.jersey.json.impl.provider.entity.JSONListElementProvider$General
com.sun.jersey.json.impl.provider.entity.JacksonProviderProxy
com.sun.jersey.moxy.MoxyMessageBodyWorker
com.sun.jersey.moxy.MoxyListMessageBodyWorker


com.sun.jersey.api.client.ClientHandlerException: A message body reader for Java class com.foo.MyFooCollectionWrapper, and Java type com.foo. MyFooCollectionWrapper<com.foo. MyFooDTO>, and MIME media type application/json was not found
  at com.sun.jersey.api.client.ClientResponse.getEntity(ClientResponse.java:550)
  at com.sun.jersey.api.client.ClientResponse.getEntity(ClientResponse.java:524)
  at com.sun.jersey.api.client.WebResource.handle(WebResource.java:686)
  at com.sun.jersey.api.client.WebResource.access$300(WebResource.java:74)
  at com.sun.jersey.api.client.WebResource$Builder.get(WebResource.java:508)

The class path on the client side test includes:

jersey-test-framework-core-1.11.jar 
jersey-test-framework-embedded-glassfish-1.11.jar 
jersey-test-framework-grizzly-1.11.jar 
jersey-test-framework-http-1.11.jar 
jersey-test-framework-inmemory-1.11.jar 
jackson-core-asl.jar 
jackson-jaxrs.jar 
jackson-xc.jar 
jackson-client.jar 
jersey-client.jar 
jersey-core.jar 
jersey-json.jar 
jettison.jar

Are my expectations wrong or am I missing something obvious here?

As a side note, if I add JAXB annotations to my entities (@XmlRootElement on MyFooCollectionWrapper and MyFooDTO) the using the same webResource get call, the client I do not get a message body reader exception, however, the response is marshaled such that MyFooCollectionWrapper looks ok but its collection does not contain a MyFooDTO it contains an XML Document with the proper values in the nodes/attrs - in other words MyFooDTP doesn't get marshaled.

When setting java.util.logging to CONFIG as was suggested in an Answer I see the following, though nothing jumps out to me. Here's a link to the output which I put on pastebin because of the length.

Thanks,

-Noah

UPDATE - SOLVED

Originally my client and client config were being created like so:

Client rootClient = new Client();
ClientConfig clientConfig = new DefaultClientConfig();
clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
Client client = new Client(rootClient, clientConfig);

When I changed this to simply

    ClientConfig clientConfig = new DefaultClientConfig();
    clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
    Client client = Client.create(clientConfig);

Things worked. It appears the rootClient was overriding the clientConfig on the new client. It seems odd that when you use a constructor that specifies a ClientConfig the ClientConfig gets overridden by the rootClients config.

Upvotes: 5

Views: 25907

Answers (4)

Ali
Ali

Reputation: 1590

you can enable POJO mapping on client side as shown below

            ClientConfig clientConfig = new DefaultClientConfig();
            clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
            Client client = Client.create(clientConfig);

            WebResource webResource = client
                    .resource("http://localhost:8080/api/url");

            ClientResponse response = webResource.accept("application/json")
                    .get(ClientResponse.class);

            if (response.getStatus() != 200) {
                throw new RuntimeException("Failed : HTTP error code : "
                        + response.getStatus());
            }

            POJOObject output = response.getEntity(POJOObject.class);

in case you have list of objects in response then you can do this

            List<POJOObject> pojoList = webResource.get(new GenericType<List<POJOObject>>() { });

you need to use these dependencies

    <dependency>
        <groupId>com.sun.jersey</groupId>
        <artifactId>jersey-client</artifactId>
        <version>1.18.1</version>
    </dependency>

    <dependency>
        <groupId>com.sun.jersey</groupId>
        <artifactId>jersey-json</artifactId>
        <version>1.18.1</version>
    </dependency>

Upvotes: 0

Christian Bongiorno
Christian Bongiorno

Reputation: 5648

You know what I find WAY easier?

MyFooDTO[] resp = webResource.accept(MediaType.APPLICATION_JSON).get(MyFooDTO[].class);

By using an array type the marshaller knows exactly what type you want to pull in and knows it's a collection. You don't get all the functionality of a Legit collection, but you can immediately iterate it and stream it.

Upvotes: 0

Brett Porter
Brett Porter

Reputation: 5867

I believe it is because MyFooDTO does not have a no-arg constructor.

If you change the java.util.logging level up to CONFIG, you should see a number of extra messages that help diagnose such problems.

Upvotes: 0

Nicolas Trichet
Nicolas Trichet

Reputation: 204

To enable POJO mapping on the client side, just do :

ClientConfig clientConfig = new DefaultClientConfig();
clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
Client client = Client.create(clientConfig);

Upvotes: 6

Related Questions