JAXB RI ClassFactory中的空指针异常 [英] Null Pointer Exception in JAXB RI ClassFactory
问题描述
我和我的朋友正在开发一个JavaFX应用程序,作为我们学校的规划师。我们有任务(课堂作业),活动,课程和学生信息。为了将数据持久存储在用户的硬盘驱动器上,我们正在使用JAXB。
My friend and I are working on a JavaFX application that acts as a planner for our school. We have tasks (homework for classes), events, courses and student info. In an attempt to store data persistently on the user's hard drive we are using JAXB.
我们已经注释了我们的类,并且可以在包装器中成功编组Task类。问题是从 tasks.xml
文件解组。
We have annotated our classes and can successfully marshall the Task class in a wrapper. The problem is unmarshalling from the tasks.xml
file.
@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:
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中的方法
它涉及从文件中保存和解组。
Method in AppData.java
It deals with saving to and unmarshalling from files.
/**
* 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()方法:
saveObject() method in the same class:
/**
* 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()
注意注释指出空指针异常
InitFiles() in App.java
Note the Comment pointing out the null pointer exception
/**
* 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
}
< h2>来自编组的格式良好的XML文档:
Well-Formed XML document from the marshalling:
<?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>
例外情况:
The exception:
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)
空指针位于<$ c $中所述的 // TODO
c> initFiles()方法:
The null pointer is at the //TODO
stated in the initFiles()
method:
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
知道。它识别任务
和TaskListWrapper
类。 - Sysouting
um.toString()
。它在内存中显示一个有效的地址,因此um
对象本身不会抛出nullpointer异常。 - 更改位置
TaskListWrapper.java
与Task.java
相同的包。 -
尝试通过将XML文件更改为只有一个
< task>
来解组单个任务,因为当我更改Things we have tried:
- Messing with the names and annotations. It doesn't seem like naming is the issue.
- Sysouting the File location to make sure it is correct.
- Sysouting the classes that the
JAXBContext
knows. It recognizes both theTask
andTaskListWrapper
classes. - Sysouting
um.toString()
. It shows a valid address in memory, so theum
object itself is not what is throwing the nullpointer exception. - Changing the location of
TaskListWrapper.java
to the same package asTask.java
. Trying to unmarshal a single Task by changing the XML file to have only one
<task>
as the root element works when I changeTaskListWrapper taskList = (TaskListWrapper) um.unmarshal(tasks);
到
Task taskList = (Task) um.unmarshal(tasks);
- http://examples.javacodegeeks.com/core-java/xml/bind/jaxb-unmarshal-example/
- 多个stackoverflow问题这与
@XMLAttribute
注释的bug有关。由于Patrick Niemeyer和Daniel Leuck不使用那些不相关的错误 -
学习Java:第4版。我们已经复制了他们设置unmarshaller的确切方法。他们有一个简单的方法:
- http://examples.javacodegeeks.com/core-java/xml/bind/jaxb-unmarshal-example/
- A multitude of stackoverflow questions which had to do with bug with the
@XMLAttribute
annotation. Since we don't use those that bug is not relevant Learning Java: 4th Edition by Patrick Niemeyer and Daniel Leuck. We have copied their exact way of setting up the unmarshaller. They have a simple approach:
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);
抛出空指针异常?
Why is TaskListWrapper taskList = (TaskListWrapper) um.unmarshal(tasks);
throwing a null pointer exception?
推荐答案
JAXB与包装器中的ObservableList等FXCollections不兼容。您必须编写一个XmlAdapter来将其解开为正常的List。所以编组将起作用但不能解组,正如你在堆栈跟踪中看到的那样:
JAXB isn't compatible to FXCollections like the ObservableList in your wrapper. You have to write an XmlAdapter to untangle it to a normal List. So marshalling will function but unmarshalling not, as you can see in the line of the stacktrace:
at com.sun.xml.internal.bind.v2.runtime.reflect.Lister$CollectionLister.startPacking(Unknown Source)
有 Lister $ CollectionLister ,它不知道如何处理未知来源。所以Adpater应该像这样使用ListWrapper:
There is the Lister$CollectionLister which don't know what to do with the Unknown Source. So an Adpater should use a ListWrapper like this:
public class TaskList {
@XmlElement(name = "task")
List<Task> entries = new ArrayList<>();
public List<Task> getEntries() {
return entries;
}
}
相应的适配器如下所示:
The corresponding Adapter look like this:
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最终应该是这样的:
So that your TaskListWrapper should finaly look like this:
//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)
注释你的类任务,并确保为每个字段设置一个getter / setter存在。就像FXProperties惯例所说:
And by the way, there are a lot of FX Properties you use, so maybe you better annotate your class Task with @XmlAccessorType(XmlAccessType.PROPERTY)
and make sure, that for every field to be set a getter/setter exist. Like the FXProperties convention says:
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表示:
The Annotation @XmlAccessType(XmlAccessorType.PROPERTY)
is described in detail here: JAXB JavaDoc. If on a package or class nothing is annotated, than the default will be @XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
, where the JavaDoc says:
每个公共getter / setter对和每个公共字段都将自动绑定到
,除非通过XmlTransient注释。
Every public getter/setter pair and every public field will be automatically bound to XML, unless annotated by XmlTransient.
因此,在FX类(特殊模型)中,您尝试在私有字段中隐藏已使用的属性。但是如果你需要一个不应该编组的公共领域呢?然后我建议做 @XmlAccessorType(XmlAccessType.PROPERTY)
注释。它的JavaDoc说:
So in a FX class (a model in special) you try to hide the used properties in private fields. But what if you need a public field that should not be marshalled? Then I recommend doing the @XmlAccessorType(XmlAccessType.PROPERTY)
annotation. The JavaDoc of it says:
JAXB绑定类中的每个getter / setter对将自动绑定到XML的
,除非通过XmlTransient注释。
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也会被考虑在内。
Watch at the little difference in one word public
, so if annotated with @XmlAccessorType(XmlAccessType.PROPERTY)
even private getter/setter will be taken into account.
但我认为大多数人使用 @XmlAccessorType(XmlAccessType.FIELD)
其中JavaDoc说:
But I think most of the people use @XmlAccessorType(XmlAccessType.FIELD)
where the JavaDoc says:
JAXB绑定类中的每个非静态非瞬态字段都将自动绑定到
,除非由XmlTransient注释。
Every non static, non transient field in a JAXB-bound class will be automatically bound to XML, unless annotated by XmlTransient.
在具有FX属性的FX类中,这可能有点棘手。我不推荐给你。
This can be a bit tricky in an FX class with FX properties. I wouldn't recommend it to you.
这篇关于JAXB RI ClassFactory中的空指针异常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!