JAXB RI ClassFactory 中的空指针异常

Posted

技术标签:

【中文标题】JAXB RI ClassFactory 中的空指针异常【英文标题】:Null Pointer Exception in JAXB RI ClassFactory 【发布时间】:2015-05-09 21:00:10 【问题描述】:

简介

我和我的朋友正在开发一个 JavaFX 应用程序,作为我们学校的规划师。我们有任务(课堂作业)、活动、课程和学生信息。为了将数据持久存储在用户的硬盘上,我们使用了 JAXB。

我们已经注释了我们的类,并且可以成功地将 Task 类编组到一个包装器中。问题是从tasks.xml 文件中解组。

以下是相关代码行:

Task.java

@XmlRootElement
public class Task 
    //constructors

    //complete constructor
    public Task(String className, String assignment, String description, LocalDate dueDate) 
        this.className = new SimpleStringProperty(className);
        this.assignment = new SimpleStringProperty(assignment);
        this.description = new SimpleStringProperty(description);

        this.dueDate = new SimpleObjectProperty<LocalDate>(dueDate);
    

    /**
     * Sets a model data into the task, sets the 
     * due date to be tomorrow.
     */
    public Task() 
        this("", "", "", LocalDate.now().plusDays(1));

        setClassName("English");
        setAssignment("Read");
        setDescription("1984");

        //setDueDate(LocalDate.now());
    
    //Instance variables

    private final SimpleStringProperty className;
    private final SimpleStringProperty assignment;
    private final SimpleStringProperty description;

    private final ObjectProperty<LocalDate> dueDate;

//  //Getters and setters

    //... Other getters and setters

    @XmlJavaTypeAdapter(LocalDateAdapter.class)
    public final java.time.LocalDate getDueDate() 
        return this.dueDateProperty().get();
    
    public final void setDueDate(final java.time.LocalDate dueDate) 
        this.dueDateProperty().set(dueDate);
    

TaskListWrapper.java:

    //used in saving the objects to XML

@XmlRootElement(name="tasks")
public class TaskListWrapper 

        private ObservableList<Task> task;

        @XmlElement(name="task")
        public ObservableList<Task> getTasks() 
            return task;
        

        public void setTasks(ObservableList<Task> tasks) 
            this.task = tasks;
        


AppData.java 中的方法

它处理文件的保存和解组。

/**
     * Save to XML using JAXB
     * @throws JAXBException 
     * @throws FileNotFoundException 
     */
    public static void save() throws JAXBException, FileNotFoundException 

        //saving other objects
        //...

        TaskListWrapper tl = new TaskListWrapper();

        //MasterTaskList is the entire list of tasks written to memory
        tl.setTasks(AppData.getMasterTaskList());

        saveObject(tl, new File(System.getProperty("user.dir") + "/resources/xml/tasks.xml"));

        saveObject(masterStudentInfo, new File(System.getProperty("user.dir") + "/resources/xml/student_info.xml"));
    

同一个类中的saveObject()方法:

/**
     * Saves a specific Object @code obj to an xml file @code xml using JAXB.
     * @param obj
     * @param xml
     * @throws FileNotFoundException
     * @throws JAXBException
     */
    private static void saveObject(Object obj, File xml) throws FileNotFoundException, JAXBException 
        //context is used to determine what kind of class is going to be marshalled or unmarshalled
        JAXBContext context = JAXBContext.newInstance(obj.getClass());

        //loads to the XML file
        Marshaller m = context.createMarshaller();
        m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);

        //loads the current list of courses to the courses.xml file
        m.marshal(obj, new FileOutputStream(xml));
    

App.java 中的 InitFiles()

注意指出空指针异常的注释

/**
     * Initial setup for all the files for the program. Contains all the
     * persistent data for the planner, such as courses, tasks, and events.
     * <p>
     * All data is saved in @code [place of installment]/resources/xml/....
     * @throws IOException
     */
    public void initFiles() throws IOException

        //... other files for other objects

        File tasks = new File(System.getProperty("user.dir") + "/resources/xml/tasks.xml");

        //check if each file exists, if so unmarshall 
        if(tasks.exists())
            try 
                JAXBContext context = JAXBContext.newInstance(TaskListWrapper.class);

                //the file location is correct
                System.out.println(tasks.toString());

                //The context knows that both the Task and TaskListWrapper classes exist
                System.out.println(context.toString());

                Unmarshaller um = context.createUnmarshaller();

                //TODO: null pointer exception
                TaskListWrapper taskList = (TaskListWrapper) um.unmarshal(tasks);
                //System.out.println(umObject.getClass());
             catch (JAXBException e) 
                e.printStackTrace();
            

         else 
            tasks.createNewFile();
        
        //... other checks for files
    

来自编组的格式良好的 XML 文档:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<tasks>
    <task>
        <assignment>Book</assignment>
        <className>Math</className>
        <description>problems</description>
        <dueDate>2015-01-17</dueDate>
    </task>
    <task>
        <assignment>Textbook</assignment>
        <className>Religion</className>
        <description>problems</description>
        <dueDate>2015-01-17</dueDate>
    </task>
    <task>
        <assignment>Read</assignment>
        <className>English</className>
        <description>1984</description>
        <dueDate>2015-03-05</dueDate>
    </task>
</tasks>

例外情况:

 java.lang.NullPointerException
    at com.sun.xml.internal.bind.v2.ClassFactory.create0(Unknown Source)
    at com.sun.xml.internal.bind.v2.ClassFactory.create(Unknown Source)
    at com.sun.xml.internal.bind.v2.runtime.reflect.Lister$CollectionLister.startPacking(Unknown Source)
    at com.sun.xml.internal.bind.v2.runtime.reflect.Lister$CollectionLister.startPacking(Unknown Source)
    at com.sun.xml.internal.bind.v2.runtime.unmarshaller.Scope.add(Unknown Source)
    at com.sun.xml.internal.bind.v2.runtime.property.ArrayERProperty$ReceiverImpl.receive(Unknown Source)
    at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.endElement(Unknown Source)
    at com.sun.xml.internal.bind.v2.runtime.unmarshaller.SAXConnector.endElement(Unknown Source)
    at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.endElement(Unknown Source)
    at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanEndElement(Unknown Source)
    at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(Unknown Source)
    at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(Unknown Source)
    at com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.next(Unknown Source)
    at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(Unknown Source)
    at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(Unknown Source)
    at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(Unknown Source)
    at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(Unknown Source)
    at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(Unknown Source)
    at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(Unknown Source)
    at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(Unknown Source)
    at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal(Unknown Source)
    at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(Unknown Source)
    at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(Unknown Source)
    at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(Unknown Source)
    at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(Unknown Source)
    at org.sjcadets.planner.App.initFiles(App.java:136)
    at org.sjcadets.planner.App.start(App.java:68)
    at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$153(Unknown Source)
    at com.sun.javafx.application.LauncherImpl$$Lambda$51/1390460753.run(Unknown Source)
    at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$166(Unknown Source)
    at com.sun.javafx.application.PlatformImpl$$Lambda$45/1051754451.run(Unknown Source)
    at com.sun.javafx.application.PlatformImpl.lambda$null$164(Unknown Source)
    at com.sun.javafx.application.PlatformImpl$$Lambda$47/231444107.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.application.PlatformImpl.lambda$runLater$165(Unknown Source)
    at com.sun.javafx.application.PlatformImpl$$Lambda$46/1775282465.run(Unknown Source)
    at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(Unknown Source)
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at com.sun.glass.ui.win.WinApplication.lambda$null$141(Unknown Source)
    at com.sun.glass.ui.win.WinApplication$$Lambda$37/1109371569.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)

空指针在initFiles()方法中声明的//TODO处:

JAXBContext context = JAXBContext.newInstance(TaskListWrapper.class);

                    //the file location is correct
                    System.out.println(tasks.toString());

                    //The context knows that both the Task and TaskListWrapper classes exist
                    System.out.println(context.toString());

                    Unmarshaller um = context.createUnmarshaller();

                    //TODO: null pointer exception
                    TaskListWrapper taskList = (TaskListWrapper) um.unmarshal(tasks);

我们尝试过的事情:

混淆了名称和注释。命名似乎不是问题。 系统输出文件位置以确保其正确。 系统化JAXBContext 知道的类。它可以识别 TaskTaskListWrapper 类。 Sysouting um.toString()。它显示了内存中的有效地址,因此um 对象本身并不是引发空指针异常的原因。 将TaskListWrapper.java 的位置更改为与Task.java 相同的包。

尝试通过将 XML 文件更改为只有一个 &lt;task&gt; 作为根元素来解组单个任务,当我更改时

TaskListWrapper taskList = (TaskListWrapper) um.unmarshal(tasks);

Task taskList = (Task) um.unmarshal(tasks);

我们寻找答案的地方:

http://examples.javacodegeeks.com/core-java/xml/bind/jaxb-unmarshal-example/ 大量 *** 问题与 @XMLAttribute 注释的错误有关。由于我们不使用那些与错误无关的内容

学习 Java:第 4 版,作者 Patrick Niemeyer 和 Daniel Leuck。我们复制了他们设置解组器的确切方法。他们有一个简单的方法:

JAXBContext context = JAXBContext.newInstance(Inventory.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
Inventory inventory = (Inventory) unmarshaller.unmarshall(
    new File("zooinventory.xml") );

问题

为什么TaskListWrapper taskList = (TaskListWrapper) um.unmarshal(tasks);会抛出空指针异常?

【问题讨论】:

我唯一能想到的可能是解组器本身是空的。您可以继续发布堆栈的其余部分吗? NullPointerException对应你代码的哪一行? um 变量是否为空? @BlaiseDoughan 当我 sysout um 它显示:com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl@2185e44d 所以我不认为它为空 @fdsa 我 sysout 了 um,它在内存中显示了一个有效的地址,因为我用 BlaiseDoughan 解决了(哈哈,对双关语感到抱歉)我在原始帖子中发布了堆栈的其余部分 【参考方案1】:

JAXB 与包装器中的 ObservableList 等 FXCollections 不兼容。您必须编写一个 XmlAdapter 才能将其与普通列表解开。所以编组会起作用,但解组不会,正如您在堆栈跟踪的行中看到的那样:

at com.sun.xml.internal.bind.v2.runtime.reflect.Lister$CollectionLister.startPacking(Unknown Source)

Lister$CollectionLister 不知道如何处理未知来源。因此,Adpater 应该像这样使用 ListWrapper:

public class TaskList 

  @XmlElement(name = "task")
  List<Task> entries = new ArrayList<>();

  public List<Task> getEntries() 
    return entries;
  

对应的Adapter如下:

public class TaskListAdapter extends XmlAdapter<TaskList, ObservableList<Task>> 

  @Override
  public ObservableList<Task> unmarshal(TaskList v) throws Exception 
    ObservableList<Task> list = FXCollections.observableArrayList(v.entries);
    return list;
  

  @Override
  public TaskList marshal(ObservableList<Task> v) throws Exception 
    TaskList taskList = new TaskList();
    v.stream().forEach((item) -> 
      taskList.entries.add(item);
    );
    return taskList;
  

这样你的 TaskListWrapper 最终应该是这样的:

//used in saving the objects to XML

@XmlRootElement(name="tasks")
public class TaskListWrapper 

        private ObservableList<Task> task;


        @XmlJavaTypeAdapter(TaskListAdapter.class)
        public ObservableList<Task> getTasks() 
            return task;
        

        public void setTasks(ObservableList<Task> tasks) 
            this.task = tasks;
        


顺便说一句,你使用了很多 FX 属性,所以也许你最好用 @XmlAccessorType(XmlAccessType.PROPERTY) 注释你的类 Task 并确保每个要设置的字段都存在一个 getter/setter。就像 FXProperties 约定所说:

private final StringProperty description = new SimpleStringProperty();
public String getDescription() 
  return description.get();

public void setDescription(String description) 
  this.description.set(description);

public StringProperty descriptionProperty()
  return description;

注解@XmlAccessType(XmlAccessorType.PROPERTY)在这里详细描述:JAXB JavaDoc。如果在包或类上没有任何注释,则默认值为@XmlAccessorType(XmlAccessType.PUBLIC_MEMBER),JavaDoc 表示:

每个公共 getter/setter 对和每个公共字段都将 自动绑定到 XML,除非被 XmlTransient 注释。

因此,在 FX 类(特殊模型)中,您尝试将使用的属性隐藏在私有字段中。但是,如果您需要一个不应编组的公共字段怎么办?然后我建议做@XmlAccessorType(XmlAccessType.PROPERTY) 注释。它的 JavaDoc 说:

JAXB 绑定类中的每个 getter/setter 对都将自动 绑定到 XML,除非由 XmlTransient 注释。

注意一个词public的细微差别,所以如果用@XmlAccessorType(XmlAccessType.PROPERTY)注释,即使是私有的getter/setter也会被考虑在内。

但我认为大多数人使用@XmlAccessorType(XmlAccessType.FIELD),JavaDoc 说:

JAXB 绑定类中的每个非静态、非瞬态字段都将是 自动绑定到 XML,除非被 XmlTransient 注释。

这在具有 FX 属性的 FX 类中可能有点棘手。我不会向你推荐它。

【讨论】:

非常感谢!那成功了。您介意解释更多@XmlAccessorType(XmlAccessType.PROPERTY) 吗?此外,XML 文档添加了另一个根 &lt;task&gt; 标记,因此它读取 &lt;tasks&gt; &lt;task&gt; &lt;task&gt; ... &lt;\task&gt; &lt;task&gt;... &lt;\task&gt; &lt;\task&gt; &lt;\tasks&gt; 我将完整的 XML 文档添加到问题中 不客气。双 标签是我的错。在 TaskListWrapper 中不应存在 @XmlElement(name = "task") 注释。删除后,它应该只添加一个任务标签。我也更正了我的答案。此外,我在上面的答案中添加了更多关于 @XmlAccessorType(XmlAccessType.PROPERTY) 的解释。 当我按照您的建议取出@XmlElement(name = "tasks") 时,额外的标签只需更改为&lt;tasks&gt;。我相信它仍在阅读 getter 和 setter 并放置任何名称。例如,getTasks() 将产生 &lt;tasks&gt;。有没有办法让 JAXB 忽略它? 我很抱歉,我认为 TaskListWrapper 类名存在一些误解。它只是用作TaskList 类。我修复了代码并删除了TaskListWrapper,因为它不需要。这摆脱了额外的&lt;tasks&gt; 标签。感谢您对其他注释的帮助和解释!

以上是关于JAXB RI ClassFactory 中的空指针异常的主要内容,如果未能解决你的问题,请参考以下文章

捕获空指针异常是代码异味吗?

Android实现滑动效果,但是其中mActionBar.setDisplayHomeAsUpEnabled(false);这句代码报空指,怎么解决

图书管理系统总结——使用到的一些工具类

org.xml.sax.SAXNotRecognizedException:无法识别功能“http://javax.xml.XMLConstants/feature/secure-processing

excel 系统错误&H80040111(-2147221231).ClassFactory无法供应请求的类

C# 中的 Jaxb 等效项