【问题标题】:Jersey JSON serialization with external classes带有外部类的 Jersey JSON 序列化
【发布时间】:2012-12-06 07:06:43
【问题描述】:

我正在尝试在我的石英调度程序之上创建一个休息 api 服务器。我希望能够将 org.quartz.Trigger 和 org.quartz.JobDetail 对象作为 JSON 返回。问题是我无法在不重新编译 jar 的情况下将 @XmlRootElement 添加到这些类中,这会导致未来升级等问题。我已经测试过并且能够在添加 @XmlRootElement 时序列化类列表但是当我尝试返回一个列表我收到错误“找不到 Java 类 java.util.ArrayList、Java 类型 java.util.List 和 MIME 媒体类型 application/json 的消息正文编写器”。我尝试添加自定义 MessageBodyWriter 但这似乎也不能解决问题。有没有办法将石英类编组为 JSON,而无需修改石英代码以添加 @XmlRootElement。我在带有码头的嵌入式 Web 服务器中使用它。

@Path("/jobs")
public class JobsResource {

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public List<Trigger> listScheduledJobs() throws SchedulerException {
        return TaskEngine.getInstance().listScheduledJobs();
    }

}

网络服务器配置

public class TaskEngineWebServer {

    private static final Logger logger = Logger.getLogger(TaskEngineWebServer.class.getName());

    private Server server;

    public TaskEngineWebServer() {
        this(8585);
    }

    public TaskEngineWebServer(Integer port) {
        server = new Server(port);

        logger.info("Configuring rest service to start at url /r");
        ServletContextHandler handler = new ServletContextHandler(ServletContextHandler.NO_SECURITY);
        //handler.getInitParams().put("com.sun.jersey.api.json.POJOMappingFeature", "true");
        PackagesResourceConfig packagesResourceConfig = new PackagesResourceConfig("com.hp.vf.scheduler.server.rest");

        ServletContainer servletContainer = new ServletContainer(packagesResourceConfig);
        handler.addServlet(new ServletHolder(servletContainer), "/r/*");

        server.setHandler(handler);
        logger.info("Done configuring rest service");
    }

    public void start() throws Exception {
        server.start();
    }

    public void stop() throws Exception {
        server.stop();
    }

    public boolean isStarted() {
        return server.isStarted();
    }

    public boolean isStopped() {
        return server.isStopped();
    }
}

【问题讨论】:

    标签: java rest jaxb jersey


    【解决方案1】:

    我认为您不能直接将 List 作为 JSON 返回。您需要有一个包含此列表的包装类。例如尝试这样的事情

    @XmlRootElement
    @XmlAccessorType(XmlAccessType.FIELD)
    public class TriggerWrapper{
    
      private List<Triggers> triggers;
    
      public List<Triggers> getTriggers(){
       if(triggers==null){
         triggers = new ArrayList<Triggers>();
       }
       return triggers;
      }
    }
    

    然后在你的休息服务课上:

    @Path("/jobs")
    public class JobsResource {
    
        @GET
        @Produces(MediaType.APPLICATION_JSON)
        public TriggerWrapper listScheduledJobs() throws SchedulerException {
            TriggerWrapper response = new TriggerWrapper();
            List<Triggers> triggers =  TaskEngine.getInstance().listScheduledJobs();
            response.getTriggers.addAll(triggers);
            return response;
        }
    
    }
    

    你的 json 应该是这样的:

    {
        "triggerwrapper": {
            "triggers": [
                {
                    "triggerid": 1
                },
                {
                    "triggerid": 2
                }
            ]
        }
    }
    

    当然,如果你愿意,你可以从你的 json 中删除根元素标签,它可以在 jersey 中配置。

    【讨论】:

    • 我在昨晚写完问题后走上了与您相同的路径,但在 Trigger 类中的子接口上遇到了问题。触发器是一个接口,它包含 JobKey 等的子接口。我开始为这些类创建@XmlJavaAdapters,但想检查答案。你提到有一种方法可以在球衣中配置它,但我不知道如何做到这一点。能给我指点一下吗。我试过 //handler.getInitParams().put("com.sun.jersey.api.json.POJOMappingFeature", "true");配置,但它不起作用。
    • 有没有更简单的方法来完成这个任务?我真的不喜欢必须为我的所有类编写 XmlJavaAdapters 的想法。
    【解决方案2】:

    我终于找到了一个干净的解决方案,它包括创建我自己的 MediaBodyWriter 类并将其添加为提供程序。您必须确保您没有使用 jersey-bundle jar,因为默认的 jaxb 到 json 提供程序将覆盖您的。

    需要罐子

    球衣核心 jersey-servlet 球衣服务器

    杰克逊注解 杰克逊数据绑定 杰克逊核心

    我在网上某个地方找到了这个 MediaWriter 示例。很抱歉没有网址,但感谢写它的人。

    @Provider
    @Produces({ MediaType.APPLICATION_JSON })
    public class JacksonWriter implements MessageBodyWriter<Object> {
        private static final ObjectMapper MAPPER = new ObjectMapper();
    
        @Override
        public boolean isWriteable(Class<?> aClass, Type type, Annotation[] annotations, MediaType mediaType) {
            return true;
        }
    
        @Override
        public long getSize(Object value, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
            return -1;
        }
    
        @Override
        public void writeTo(Object value, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, Object> httpHeaders,
                OutputStream entityStream) {
            try {
                MAPPER.writeValue(entityStream, value);
            } catch (IOException ex) {
                throw new RuntimeException(ex);
            }
        }
    }
    

    加载时,您将看到一条日志消息,表明您的提供程序已加载。

    这给了我期望的 json 输出,因为它不依赖于 JAXB 注释,而只是使用对象映射器/反射。可能效率较低,但对我来说没关系。

    【讨论】:

      猜你喜欢
      • 2012-11-18
      • 2019-07-25
      • 2018-12-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-24
      • 2014-01-20
      相关资源
      最近更新 更多