JAXB RI ClassFactory 中的空指针异常
Null Pointer Exception in JAXB RI ClassFactory
简介
我和我的朋友正在开发一个 JavaFX 应用程序,它充当我们学校的规划师。我们有任务(classes 的家庭作业)、活动、课程和学生信息。为了尝试将数据持久存储在用户的硬盘上,我们正在使用 JAXB。
我们已经注释了我们的 classes 并且可以在包装器中成功编组任务 class。问题是从 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"));
}
同一个 class:
中的 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$launchApplication13(Unknown Source)
at com.sun.javafx.application.LauncherImpl$$Lambda/1390460753.run(Unknown Source)
at com.sun.javafx.application.PlatformImpl.lambda$runAndWait6(Unknown Source)
at com.sun.javafx.application.PlatformImpl$$Lambda/1051754451.run(Unknown Source)
at com.sun.javafx.application.PlatformImpl.lambda$null4(Unknown Source)
at com.sun.javafx.application.PlatformImpl$$Lambda/231444107.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl.lambda$runLater5(Unknown Source)
at com.sun.javafx.application.PlatformImpl$$Lambda/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$null1(Unknown Source)
at com.sun.glass.ui.win.WinApplication$$Lambda/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);
我们尝试过的事情:
- 混淆名称和注释。命名似乎不是问题。
- 正在检查文件位置以确保它是正确的。
- Sysouting
JAXBContext
知道的 classes。它识别 Task
和 TaskListWrapper
classes.
- 系统调度
um.toString()
。它显示内存中的有效地址,因此 um
对象本身并不是引发空指针异常的对象。
- 正在将
TaskListWrapper.java
的位置更改为与 Task.java
相同的包。
试图通过将 XML 文件更改为只有一个 <task>
作为根元素来解组单个任务,当我更改
TaskListWrapper taskList = (TaskListWrapper) um.unmarshal(tasks);
到
Task taskList = (Task) um.unmarshal(tasks);
我们寻找答案的地方:
- http://examples.javacodegeeks.com/core-java/xml/bind/jaxb-unmarshal-example/
- 大量与
@XMLAttribute
注释错误有关的 Whosebug 问题。因为我们不使用那些与错误无关的东西
学习 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);
抛出空指针异常?
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;
}
}
相应的适配器如下所示:
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)
注释您的 class 任务,并确保为每个字段设置一个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。如果在包或 class 上没有任何注释,则默认值为 @XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
,其中 JavaDoc 说:
Every public getter/setter pair and every public field will be
automatically bound to XML, unless annotated by XmlTransient.
因此在 FX class(特殊模型)中,您尝试将使用的属性隐藏在私有字段中。但是,如果您需要一个不应编组的 public 字段怎么办?然后我建议做 @XmlAccessorType(XmlAccessType.PROPERTY)
注释。它的 JavaDoc 说:
Every getter/setter pair in a JAXB-bound class will be automatically
bound to XML, unless annotated by XmlTransient.
注意一个字的细微差别public
,所以如果用@XmlAccessorType(XmlAccessType.PROPERTY)
注释,即使是私有的getter/setter也会被考虑。
但我认为大多数人都使用 @XmlAccessorType(XmlAccessType.FIELD)
,其中 JavaDoc 说:
Every non static, non transient field in a JAXB-bound class will be
automatically bound to XML, unless annotated by XmlTransient.
这在具有 FX 属性的 FX class 中可能有点棘手。我不会推荐给你。
简介
我和我的朋友正在开发一个 JavaFX 应用程序,它充当我们学校的规划师。我们有任务(classes 的家庭作业)、活动、课程和学生信息。为了尝试将数据持久存储在用户的硬盘上,我们正在使用 JAXB。
我们已经注释了我们的 classes 并且可以在包装器中成功编组任务 class。问题是从 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"));
}
同一个 class:
中的 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$launchApplication13(Unknown Source)
at com.sun.javafx.application.LauncherImpl$$Lambda/1390460753.run(Unknown Source)
at com.sun.javafx.application.PlatformImpl.lambda$runAndWait6(Unknown Source)
at com.sun.javafx.application.PlatformImpl$$Lambda/1051754451.run(Unknown Source)
at com.sun.javafx.application.PlatformImpl.lambda$null4(Unknown Source)
at com.sun.javafx.application.PlatformImpl$$Lambda/231444107.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl.lambda$runLater5(Unknown Source)
at com.sun.javafx.application.PlatformImpl$$Lambda/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$null1(Unknown Source)
at com.sun.glass.ui.win.WinApplication$$Lambda/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);
我们尝试过的事情:
- 混淆名称和注释。命名似乎不是问题。
- 正在检查文件位置以确保它是正确的。
- Sysouting
JAXBContext
知道的 classes。它识别Task
和TaskListWrapper
classes. - 系统调度
um.toString()
。它显示内存中的有效地址,因此um
对象本身并不是引发空指针异常的对象。 - 正在将
TaskListWrapper.java
的位置更改为与Task.java
相同的包。 试图通过将 XML 文件更改为只有一个
<task>
作为根元素来解组单个任务,当我更改TaskListWrapper taskList = (TaskListWrapper) um.unmarshal(tasks);
到
Task taskList = (Task) um.unmarshal(tasks);
我们寻找答案的地方:
- http://examples.javacodegeeks.com/core-java/xml/bind/jaxb-unmarshal-example/
- 大量与
@XMLAttribute
注释错误有关的 Whosebug 问题。因为我们不使用那些与错误无关的东西 学习 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);
抛出空指针异常?
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;
}
}
相应的适配器如下所示:
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)
注释您的 class 任务,并确保为每个字段设置一个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。如果在包或 class 上没有任何注释,则默认值为 @XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
,其中 JavaDoc 说:
Every public getter/setter pair and every public field will be automatically bound to XML, unless annotated by XmlTransient.
因此在 FX class(特殊模型)中,您尝试将使用的属性隐藏在私有字段中。但是,如果您需要一个不应编组的 public 字段怎么办?然后我建议做 @XmlAccessorType(XmlAccessType.PROPERTY)
注释。它的 JavaDoc 说:
Every getter/setter pair in a JAXB-bound class will be automatically bound to XML, unless annotated by XmlTransient.
注意一个字的细微差别public
,所以如果用@XmlAccessorType(XmlAccessType.PROPERTY)
注释,即使是私有的getter/setter也会被考虑。
但我认为大多数人都使用 @XmlAccessorType(XmlAccessType.FIELD)
,其中 JavaDoc 说:
Every non static, non transient field in a JAXB-bound class will be automatically bound to XML, unless annotated by XmlTransient.
这在具有 FX 属性的 FX class 中可能有点棘手。我不会推荐给你。