jakarta.xml.bind.ContextFinder.handleClassCastException 处的 java.lang.NullPointerException

标签 java rest jaxb jersey marshalling

我对构建 REST API 应用程序非常陌生。这里我试图让一个简单的方法返回模型对象 Message

的响应 xml

这是我的 web.xml

<?xml version="1.0" encoding="UTF-8"?>
<!-- This web.xml file is not required when using Servlet 3.0 container,
     see implementation details http://jersey.java.net/nonav/documentation/latest/jax-rs.html -->
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
    <servlet>
        <servlet-name>Jersey Web Application</servlet-name>
        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
        <init-param>
            <param-name>jersey.config.server.provider.packages</param-name>
            <param-value>com.rest.messenger.service</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>Jersey Web Application</servlet-name>
        <url-pattern>/webapi/*</url-pattern>
    </servlet-mapping>
</web-app>

我有一个服务类,它返回 Message 对象的硬编码列表。

package com.rest.messenger.service;

import java.util.ArrayList;
import java.util.List;

import com.rest.messenger.model.Message;

public class MessageService {


    public List<Message> getAllMessages(){

        Message m1 = new Message(1L, "message1", "mt");
        Message m2 = new Message( 2L, "message 2", "pt");
        List<Message> messages = new ArrayList<Message>();
        messages.add(m1);
        messages.add(m2);
        return messages;

    }
}

Message 对象定义如下(这里我使用 @XmlRootElement 注释让 jaxb 将其转换为 xml,因为我希望资源返回 xml 响应):

package com.rest.messenger.model;

import java.util.Date;

import jakarta.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Message {

    private long id;
    private String message;
    private String author;
    private Date created;

    public Message() {

    }
    public Message(long id, String message, String author) {
        super();
        this.id = id;
        this.message = message;
        this.author = author;
        this.created = new Date();
    }
    public long getId() {
        return id;
    }
    public void setId(long id) {
        this.id = id;
    }
    public String getMessage() {
        return message;
    }
    public void setMessage(String message) {
        this.message = message;
    }
    public String getAuthor() {
        return author;
    }
    public void setAuthor(String author) {
        this.author = author;
    }
    public Date getCreated() {
        return created;
    }
    public void setCreated(Date created) {
        this.created = created;
    }
}

我将传入的 GET 请求映射到资源,如下所示:

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

@Path("/messages")
public class MessageResource {

    public MessageService messageService = new MessageService();
    @GET
    @Produces(MediaType.APPLICATION_XML)
    public List<Message> getMessages() {

        return messageService.getAllMessages();
    }

}

现在,当我运行它并尝试访问 URL http://localhost:8080/messenger.service/webapi/messages

处的资源时

我收到以下内部服务器错误:

May 06, 2020 1:28:14 PM org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet [Jersey Web Application] in context with path [/messenger.service] threw exception [java.lang.NullPointerException] with root cause
java.lang.NullPointerException
    at jakarta.xml.bind.ContextFinder.handleClassCastException(ContextFinder.java:114)
    at jakarta.xml.bind.ContextFinder.newInstance(ContextFinder.java:254)
    at jakarta.xml.bind.ContextFinder.newInstance(ContextFinder.java:240)
    at jakarta.xml.bind.ContextFinder.find(ContextFinder.java:375)
    at jakarta.xml.bind.JAXBContext.newInstance(JAXBContext.java:691)
    at jakarta.xml.bind.JAXBContext.newInstance(JAXBContext.java:632)
    at org.glassfish.jersey.jaxb.internal.AbstractJaxbProvider.getStoredJaxbContext(AbstractJaxbProvider.java:288)
    at org.glassfish.jersey.jaxb.internal.AbstractJaxbProvider.getJAXBContext(AbstractJaxbProvider.java:273)
    at org.glassfish.jersey.jaxb.internal.AbstractJaxbProvider.getMarshaller(AbstractJaxbProvider.java:240)
    at org.glassfish.jersey.jaxb.internal.AbstractJaxbProvider.getMarshaller(AbstractJaxbProvider.java:207)
    at org.glassfish.jersey.jaxb.internal.AbstractCollectionJaxbProvider.writeTo(AbstractCollectionJaxbProvider.java:243)
    at org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor.invokeWriteTo(WriterInterceptorExecutor.java:242)

我遵循的教程使用了 javax,我相信它现在已迁移到 jakarta。 不确定是否与@XmlRootElement 有关 我还在 pom.xml 中添加了依赖项,如下建议: jersey return 500 when trying return an XML

按照建议,我使用模型对象字段的修饰符将其设为私有(private)。(使用 jaxb 的默认 XML 访问器类型能够在私有(private)字段上工作)

有人可以对此提出建议吗?

最佳答案

经过多次尝试解决这个问题,我得到了解决方案:

有关使用 JAXB 时导致 java.lang.NullPointerException 的背景情况以及显示 handleClassCastException 的堆栈跟踪可以在以下位置找到: https://github.com/javaee/jaxb-v2/issues/863发生类似的事情。

所以基本上,JAXB 同时存在多个 API 副本。我的应用程序将 Jakarta API 作为 jersey 依赖项的一部分(Jakarta 现在是 Java EE 的标准 API),但直到 JDK 8,JAXB 也是类路径中 Java 扩展 API 的一部分(javax 支持)。我使用 jakarta api 导入 JAXB 注释和类,但也使用 jdk 8 进行编译,从而导致了问题。在使用 jersey 时,我使用 Jersey 3.0.0 的 pom 依赖项来实现 REST API(它仍然“太新”,不能被视为稳定版本)

如果您打算坚持使用 JDK 8,解决方案可能是对所有 api 和导入使用 javax - 并降级 pom 中的 jersey 版本(我使用 2.3.1)[ 或外部JAR 版本(如果您没有使用 Maven)

请注意,更高的 jersey 版本 - 3.0.0 仅与 jakarta API 捆绑在一起,在这种情况下,您必须将导入从 javax 转移到 jakarta。

第二个选项是,如果您打算升级高于 1.8 的 JDK 版本,您应该收到 NoClassDefFoundError,但如果您仍然使用不大于 10 的 JDK 版本,则可以解决该问题。 Jaxb API 现在应该是 Java EE 的 API,并且类路径默认不包含它们。然而,有一种方法可以启用它们: How to resolve java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException in Java 9

就我而言,我通过在 pom.xml 中添加条目来提供对 JAXB 实现类的支持:

总而言之,我做了以下事情:

  • 我继续将 JDK 10 与 jersey 2.3.1 结合使用
  • 通过在 POM 中添加依赖项提供对 JAXB 的支持:
        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>2.3.1</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jaxb</groupId>
            <artifactId>jaxb-runtime</artifactId>
            <version>2.3.1</version>
            <scope>runtime</scope>
        </dependency>

关于jakarta.xml.bind.ContextFinder.handleClassCastException 处的 java.lang.NullPointerException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61630890/

相关文章:

java - REST添加子资源

java - HTTP请求参数类型传播

java - JAXB 过滤解析

java - 使用 JAXB 处理设计不佳的 XML

java - 我正在学习做 Restful 网络服务,同时实现我得到以下错误帮助我解决

java - 如果一个类有@XmlElement 属性,它不能有@XmlValue 属性

Java - MySQL 到 Hive 的导入,其中 MySQL 在 Windows 上运行,Hive 在 Centos 上运行(Horton 沙盒)

java - 在ubuntu服务器上启动小型java解析应用程序

Java从字符串中提取字符串

java - 在 ClassPathXmlApplicationContext 中失败时销毁创建的 bean