【问题标题】:JAX-RS Jersey Client marshaling JSON response with POJO MAPPING & JacksonJAX-RS Jersey 客户端使用 POJO MAPPING 和 Jackson 编组 JSON 响应
【发布时间】:2012-06-07 02:43:58
【问题描述】:

我在使用 JSONConfiguration.FEATURE_POJO_MAPPING 设置为 true 的 Jersey 客户端 (1.11) 时遇到了一点问题。我的测试代码如下所示:

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

在服务器上:

1) 我的 web.xml 将 POJO 映射设置为 true。

2) MyFooDTO 只是一个 POJO,如下所示:

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 看起来像这样:

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 ..

}

我已验证服务器在创建 Json 响应时没有问题。如果我将响应类型设置为字符串,我可以使用我的 Jersey 客户端代码进行检索。当我使用

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

我希望 POJO 映射能够正常工作(编组响应),而不需要任何自定义消息正文阅读器。但是,我得到:

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)

客户端测试的类路径包括:

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

我的期望是错误的还是我在这里遗漏了一些明显的东西?

作为旁注,如果我将 JAXB 注释添加到我的实体(MyFooCollectionWrapper 和 MyFooDTO 上的 @XmlRootElement)使用相同的 webResource get 调用,客户端我没有收到消息正文阅读器异常,但是,响应被编组这样 MyFooCollectionWrapper 看起来不错,但它的集合不包含 MyFooDTO 它包含一个 XML 文档,其中节点/属性中具有正确的值 - 换句话说,MyFooDTP 不会被编组。

当按照答案中的建议将 java.util.logging 设置为 CONFIG 时,我看到以下内容,但没有任何反应。这是一个link,由于长度原因,我把它放在了pastebin上。

谢谢,

-诺亚

更新 - 已解决

最初我的客户端和客户端配置是这样创建的:

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

当我把它改成简单的时候

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

一切顺利。看来 rootClient 正在覆盖新客户端上的 clientConfig。当您使用指定 ClientConfig 的构造函数时,ClientConfig 会被 rootClients 配置覆盖,这似乎很奇怪。

【问题讨论】:

    标签: java json jersey jax-rs jackson


    【解决方案1】:

    要在客户端启用 POJO 映射,只需:

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

    【讨论】:

    • 我已经在做这一切了。这就是我上面问题的第一句话,“我在使用 JSONConfiguration.FEATURE_POJO_MAPPING 设置为 true 的 Jersey 客户端 (1.11) 时遇到了一点问题”。
    • 这种确切的方法有效。有关错误的详细信息,请参阅上面我修改过的 cmets。
    • 好答案。这在配置 JerseyTest 时也很重要,否则只有 JerseyTest 的服务器部分可以与 POJO 一起使用,并且您的测试类中的客户端将失败:java.net/projects/jersey/lists/users/archive/2011-07/message/43
    【解决方案2】:

    我相信这是因为MyFooDTO 没有无参数构造函数。

    如果您将 java.util.logging 级别更改为 CONFIG,您应该会看到许多有助于诊断此类问题的额外消息。

    【讨论】:

    • 我的代码实际上确实没有 arg 构造函数,但我将它排除在上面的示例之外,所以我添加了它。所以即使它确实没有参数,我仍然有这个问题。
    • 我添加了 CONFIG 输出。没有什么是问题。
    【解决方案3】:

    你知道我发现什么更容易吗?

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

    通过使用数组类型,编组器确切地知道你想要拉入什么类型并且知道它是一个集合。您无法获得合法集合的所有功能,但您可以立即对其进行迭代和流式传输。

    【讨论】:

      【解决方案4】:

      您可以在客户端启用 POJO 映射,如下所示

                  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);
      

      如果您有响应的对象列表,那么您可以这样做

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

      你需要使用这些依赖项

          <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>
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-09-20
        • 2016-01-10
        • 1970-01-01
        • 2013-02-17
        • 2015-07-20
        • 1970-01-01
        • 2019-09-25
        相关资源
        最近更新 更多