Java 无法使用Jersey客户端解组对象的JSON数组

Java 无法使用Jersey客户端解组对象的JSON数组,java,json,jaxb,jersey,Java,Json,Jaxb,Jersey,我正在尝试解组的一个单元素JSON数组: [ { "id":"42", "status":"Active", "name":"purple monkey dishwasher" } ] 相应的Java类(为简洁起见省略了getter和setter): Jersey客户端代码,它发出HTTP请求,并应将上述JSON解组为一个元素列表: 我在Badge上尝试了多种注释组合,或者使用数组代替GenericType: List<Badge>

我正在尝试解组的一个单元素JSON数组:

[
   {
      "id":"42",
      "status":"Active",
      "name":"purple monkey dishwasher"
   }
]
相应的Java类(为简洁起见省略了getter和setter):

Jersey客户端代码,它发出HTTP请求,并应将上述JSON解组为一个元素
列表

我在
Badge
上尝试了多种注释组合,或者使用数组代替
GenericType

List<Badge> badges = Arrays.asList(apiRoot.path("/badges").get(Badge[].class));
但到目前为止,还没有人解决这个问题

更令人困惑的是,我现有的设置在解开JSON编码的
Badge
s(位于其他结构内部)时没有问题,如下所示:

{
   "userid":"123456789",
   "userbadges":[
      {
         "badge":{
              "id":"42",
              "status":"Active",
              "name":"purple monkey dishwasher"
         },
         "earned":"2012-03-06 18:16:18.172"
      }
   ]
}

我做错了什么?

这可能与制作人没有将单例列表正确编码为JSON有关。有关更全面的解释和建议的解决方案,请参阅

根据本文所描述的内容,并根据错误消息,我猜测将生成以下内容:

{
   {
      "id":"42",
      "status":"Active",
      "name":"purple monkey dishwasher"
   }
}
根据这篇文章,解决方案在于扩展和定制提供者,以更正单例列表和空列表如何格式化为JSON

不幸的是,这篇文章是用德语写的,我必须自己翻译——如果它不能真正解决你的问题,请告诉我。如果是这样的话,这篇文章的作者德克·迪特马尔(Dirk Dittmar)就功不可没


PS-如果你像我一样使用Chrome来翻译页面,请确保切换回原始页面,以查看代码片段,因为其中的一部分被错误地“翻译”为空白。

注意:我是专家组的负责人和成员

您可以使用添加到EclipseLink 2.4中MOXy组件的JSON绑定扩展来处理此用例:

演示

Jersey客户端API允许您从客户端的服务器端利用相同的
MessageBodyReader
/
MessageBodyWriter

package forum9627170;

import java.util.List;
import org.example.Customer;
import com.sun.jersey.api.client.*;
import com.sun.jersey.api.client.config.*;

public class Demo {

    public static void main(String[] args) {
        ClientConfig cc = new DefaultClientConfig();
        cc.getClasses().add(MOXyJSONProvider.class);
        Client client = Client.create(cc);
        WebResource apiRoot = client.resource("http://localhost:9000/api");
        List<Badge> badges = apiRoot.path("/badges").accept("application/json").get(new GenericType<List<Badge>>(){});

        for(Badge badge : badges) {
            System.out.println(badge.getId());
        }
    }

}

了解更多信息


更新 在GlassFish 4中,EclipseLink JAXB(MOXy)是Jersey使用的默认JSON绑定提供程序:


我能够通过使用作为Jersey客户端实例的
MessageBody(Reader | Writer)
提供者,以最小的努力解决这个问题:

ClientConfig cfg = new DefaultClientConfig();
cfg.getClasses().add(JacksonJsonProvider.class);
Client client = Client.create(cfg);
Jackson的
MessageBodyReader
实现似乎比Jersey JSON实现表现得更好


感谢您为我指明了Jackson的方向。

默认情况下,Jersey使用JAXB进行(un)编组过程,不幸的是,JAXB JSON处理器不是标准的(忽略单元素数组,将空数组转换为单元素空数组…)

因此,您有两个选择:

  • 将JAXB配置为更标准(更多信息)
  • 使用Jackson而不是JAXB——我建议这样做
  • 通过以下方式使用Jackson客户端:

    ClientConfig clientConfig = new DefaultClientConfig();
    clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
    Client client = Client.create(clientConfig);
    List<Badge> badges = client.resource("/badges").getEntity(new GenericType<List<Badge>>() {});
    
    ClientConfig ClientConfig=newdefaultclientconfig();
    clientConfig.getFeatures().put(JSONConfiguration.FEATURE\u POJO\u映射,Boolean.TRUE);
    Client=Client.create(clientConfig);
    List badges=client.resource(“/badges”).getEntity(新的GenericType(){});
    
    我有一个类似的问题,并通过以下方法得到解决

  • 像这样制作一个JAXB上下文解析器

    import java.util.ArrayList;
    import java.util.List;
    
    import javax.ws.rs.ext.ContextResolver;
    import javax.ws.rs.ext.Provider;
    import javax.xml.bind.JAXBContext;
    
    import com.sun.jersey.api.json.JSONConfiguration;
    import com.sun.jersey.api.json.JSONJAXBContext;
    
    @Provider
    public class JAXBContextResolver implements ContextResolver<JAXBContext> {
    
        private JAXBContext       context;
    
        private Class<?>[]        types    = { Badge.class };
    
        private List<Class<?>>    classes    = new ArrayList<Class<?>>();
    
        public JAXBContextResolver() throws Exception {
            this.context = new JSONJAXBContext(JSONConfiguration.natural().build(), types);
    
            for (Class<?> clazz : types) {
                classes.add(clazz);
            }
        }
    
        public JAXBContext getContext(Class<?> objectType) {
            return classes.contains(objectType) ? context : null;
        }
    
    }
    
  • 现在您可以获得对象数组

    WebResource apiRoot = client.resource("http://localhost:9000/api");
    Badge[] badges = apiRoot.path("/badges").get(Badge[].class);
    
  • 如果您想要一个列表,只需使用

       Arrays.asList(badges)
    
    导入这个

    
    泽西岛
    泽西json
    1.17
    编译
    
    这是破译密码

    import com.sun.jersey.api.json.JSONJAXBContext;
    import com.sun.jersey.api.json.JSONUnmarshaller;
    public static <T> T unmarshalJson(String jsonTxt, Class<T> clazz) throws JAXBException {
        JSONJAXBContext jctx = new JSONJAXBContext(clazz);
        JSONUnmarshaller unm = jctx.createJSONUnmarshaller();
        return (T)unm.unmarshalFromJSON(new StringReader(jsonTxt), clazz);
    }
    
    import com.sun.jersey.api.json.JSONJAXBContext;
    导入com.sun.jersey.api.json.jsonumarshaller;
    公共静态T unmarshalJson(字符串jsonTxt,类clazz)抛出JAXBEException{
    JSONJAXBContext jctx=新的JSONJAXBContext(clazz);
    jsonumarshaller unm=jctx.createjsonumarshaller();
    返回(T)unm.unmarshalFromJSON(新的StringReader(jsonTxt),clazz);
    }
    
    谢谢,我来试一试。我很想知道为什么这不能开箱即用——你知道吗?另外,我知道在你发布答案之前只是时间问题。:)@MΓΓББLL-我已经更新了我的答案。我相信它不是开箱即用的,因为你的收藏中的物品没有
    徽章
    作为根元素。哦,很有趣。再看一看JSON我能解组和JSON我不能解组之间的区别,我明白你的意思了。这种方法不是标准方法。要在Jersey中生成和使用标准JSON,必须使用Jackson!这很简单、快速,Jersey团队就是这样设计的。@yvesamsellem-(-1?)。这种方法使用标准的“MessageBodyReader/Writer”接口与Jersey客户端API交互,并清楚地表明您不需要使用Jackson在Jersey中生成和使用标准JSON。GlassFish()中已经有了MOXy的早期版本,而包含JSON绑定的版本将包含在未来的GlassFish版本中。堆栈跟踪清楚地表明JAXB实现与JSON一起使用,MOXy比Jackson支持更多的JAXB注释。是的,正如前面所说,我正在尝试Jackson,这似乎不那么奇怪。很高兴知道。仅当您希望自定义(取消)编组过程时,才需要使用自定义JacksonProvider。如果不这样做,使用Jersey提供的默认提供程序就足够了(JSONConfiguration功能)。事实上,我使用的是一个自定义提供程序,这样我就可以在Jackson注释旁边使用JAXB注释:默认Jersey Jackson提供程序配置为在Jackson注释旁边使用JAXB注释;-)唯一的问题是没有记录此提供程序的确切配置:-(我(基本上)只是使用
    JacksonJsonProvider
    @mΓΓΓБLL,而不是繁琐地创建我自己的提供程序-听起来不错!感谢您的关注
    ClientConfig clientConfig = new DefaultClientConfig();
    clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
    Client client = Client.create(clientConfig);
    List<Badge> badges = client.resource("/badges").getEntity(new GenericType<List<Badge>>() {});
    
    import java.util.ArrayList;
    import java.util.List;
    
    import javax.ws.rs.ext.ContextResolver;
    import javax.ws.rs.ext.Provider;
    import javax.xml.bind.JAXBContext;
    
    import com.sun.jersey.api.json.JSONConfiguration;
    import com.sun.jersey.api.json.JSONJAXBContext;
    
    @Provider
    public class JAXBContextResolver implements ContextResolver<JAXBContext> {
    
        private JAXBContext       context;
    
        private Class<?>[]        types    = { Badge.class };
    
        private List<Class<?>>    classes    = new ArrayList<Class<?>>();
    
        public JAXBContextResolver() throws Exception {
            this.context = new JSONJAXBContext(JSONConfiguration.natural().build(), types);
    
            for (Class<?> clazz : types) {
                classes.add(clazz);
            }
        }
    
        public JAXBContext getContext(Class<?> objectType) {
            return classes.contains(objectType) ? context : null;
        }
    
    }
    
    ClientConfig config = new DefaultClientConfig();
    config.getClasses().add(JAXBContextResolver.class);
    
    Client client = Client.create(config);
    
    WebResource apiRoot = client.resource("http://localhost:9000/api");
    Badge[] badges = apiRoot.path("/badges").get(Badge[].class);
    
       Arrays.asList(badges)
    
    import com.sun.jersey.api.json.JSONJAXBContext;
    import com.sun.jersey.api.json.JSONUnmarshaller;
    public static <T> T unmarshalJson(String jsonTxt, Class<T> clazz) throws JAXBException {
        JSONJAXBContext jctx = new JSONJAXBContext(clazz);
        JSONUnmarshaller unm = jctx.createJSONUnmarshaller();
        return (T)unm.unmarshalFromJSON(new StringReader(jsonTxt), clazz);
    }