Null Pointer Exception in JAXB RI ClassFactory

懵懂的女人 提交于 2019-12-05 04:01:35

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)

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;
  }
}

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;
        }

}

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;
}

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:

Every public getter/setter pair and every public field will be automatically bound to XML, unless annotated by XmlTransient.

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:

Every getter/setter pair in a JAXB-bound class will be automatically bound to XML, unless annotated by XmlTransient.

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.

But I think most of the people use @XmlAccessorType(XmlAccessType.FIELD) where the JavaDoc says:

Every non static, non transient field in a JAXB-bound class will be automatically bound to XML, unless annotated by XmlTransient.

This can be a bit tricky in an FX class with FX properties. I wouldn't recommend it to you.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!