我正在编写一个简单的预算程序,该程序具有带有类别类数组的预算类。每个类别类都可以有子类别。当我尝试使用JAXB将数据保存到XML文件时,我收到错误 com.sun.istack.internal.SAXException2:在对象图中检测到一个循环。这将导致无限深的XML
我搜索了错误并看到它是由父子关系引起的,其中父级引用子级并且子级引用父级。大多数答案都是使用@XMLTransient。
我的问题是我的Category类既不引用预算父类,也不引用父类别(如果存在)。
我是JAXB的新手,但不是Java。我使用这个应用程序作为JAXB和JavaFX的学习经验。
以下是我的预算和类别课程。
package budget.model;
import java.time.LocalDate;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import budget.util.BudgetProperties.DayOfWeek;
import budget.util.LocalDateAdapter;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
@XmlRootElement(name = "budget")
public class Budget {
// default to Sunday
ObjectProperty<DayOfWeek> startOfWeek = new SimpleObjectProperty<DayOfWeek>(DayOfWeek.SUNDAY);
ObjectProperty<LocalDate> startDate = new SimpleObjectProperty<LocalDate>();
IntegerProperty daysBeyondWeek = new SimpleIntegerProperty(3);
IntegerProperty numberOfWeeks = new SimpleIntegerProperty();
ObservableList<Category> categories = FXCollections.observableArrayList();
// startOfWeek
public DayOfWeek getStartOfWeek() {
return this.startOfWeek.getValue();
}
public void setStartOfWeek(DayOfWeek startOfWeek) {
this.startOfWeek.set(startOfWeek);
}
public ObjectProperty<DayOfWeek> startOfWeekProperty() {
return this.startOfWeek;
}
// startDate
@XmlJavaTypeAdapter(LocalDateAdapter.class)
public LocalDate getStartDate() {
return this.startDate.getValue();
}
public void setStartDate(LocalDate startDate){
this.startDate.set(startDate);
}
public ObjectProperty<LocalDate> startDateProperty() {
return this.startDate;
}
// daysBeyondWeek
public Integer getDaysBeyondWeek() {
return this.daysBeyondWeek.getValue();
}
public void setDaysBeyondWeek(Integer daysBeyondWeek) {
this.daysBeyondWeek.set(daysBeyondWeek);
}
public IntegerProperty daysBeyondWeekProperty() {
return this.daysBeyondWeek;
}
// numberOFWeeks
public Integer getNumberOfWeeks() {
return this.numberOfWeeks.getValue();
}
public void setNumberOfWeeks(Integer numberOfWeeks) {
this.numberOfWeeks.set(numberOfWeeks);
}
public IntegerProperty numberOfWeeksProperty() {
return numberOfWeeks;
}
// categories
public ObservableList<Category> getCategories () {
return categories;
}
public void setCategories(ObservableList<Category> categories) {
this.categories = categories;
}
public ObservableList<Category> categoriesProperty () {
return categories;
}
}
package budget.model;
import java.time.LocalDate;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import budget.util.BudgetProperties.RepeatFrequency;
import budget.util.LocalDateAdapter;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
public class Category {
StringProperty name = new SimpleStringProperty("");
ObservableList<Category> children = FXCollections.observableArrayList();
StringProperty comments = new SimpleStringProperty("");
ObjectProperty<RepeatFrequency> repeatFrequency = new SimpleObjectProperty<RepeatFrequency>();
ObjectProperty<LocalDate> dueDate = new SimpleObjectProperty<LocalDate>();
DoubleProperty budgetAmount = new SimpleDoubleProperty();
DoubleProperty actualAmount = new SimpleDoubleProperty();
// name
public String getName() {
return name.getValue();
}
public void setName(String name) {
this.name.set(name);
}
public StringProperty nameProperty() {
return name;
}
// children
public ObservableList<Category> getChildren() {
return children;
}
public void setChildren(ObservableList<Category> children) {
this.children.setAll(children);
}
public void addChild(Category category) {
this.children.add(category);
}
// isParent
public Boolean isParent() {
// return this.parent.getValue();
if (children == null || children.isEmpty() || children.size() == 0) {
return false;
} else {
return true;
}
}
// comments
public String getComments() {
return comments.getValue();
}
public void setComments(String comments) {
this.comments.set(comments);
}
public StringProperty commentsProperty() {
return comments;
}
// repeatFrequency
public RepeatFrequency getRepeatFrequency() {
return this.repeatFrequency.getValue();
}
public void setRepeatFrequency(RepeatFrequency repeatFrequency) {
this.repeatFrequency.set(repeatFrequency);
}
public ObjectProperty<RepeatFrequency> repeatFrequencyProperty() {
return this.repeatFrequency;
}
// dueDate
@XmlJavaTypeAdapter(LocalDateAdapter.class)
public LocalDate getDueDate() {
return this.dueDate.getValue();
}
public void setDueDate(LocalDate dueDate) {
this.dueDate.set(dueDate);
}
public ObjectProperty<LocalDate> dueDateProperty() {
return this.dueDate;
}
// budgetAmount
public Double getBudgetAmount() {
return this.budgetAmount.getValue();
}
public void setBudgetAmount(Double budgetAmount) {
this.budgetAmount.set(budgetAmount);
}
public DoubleProperty budgetAmountProperty() {
return this.budgetAmount;
}
// actualAmount
public Double getActualAmount() {
return this.actualAmount.getValue();
}
public void setActualAmount(Double actualAmount) {
this.actualAmount.set(actualAmount);
}
public DoubleProperty actualAmountProperty() {
return this.actualAmount;
}
}
还有另一个类处理编组。此类中的函数位于
之下 public void saveBudgetData(Budget budget) {
File file = new File(path + BUDGET_FILE);
try {
JAXBContext context = JAXBContext
.newInstance(Budget.class);
Marshaller m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
// Marshalling and saving XML to the file.
m.marshal(budget, file);
} catch (Exception e) { // catches ANY exception
logger.error("exception: ", e);
Alert alert = new Alert(AlertType.ERROR);
alert.setTitle("Error");
alert.setHeaderText("Could not save data");
alert.setContentText("Could not save data to file:\n" + file.getPath());
alert.showAndWait();
}
}
我知道这是类别之间的递归关系。这是抱怨的吗?我没有在我的搜索中看到这个场景。
感谢。
清理预算和类别
package budget.model;
import java.time.LocalDate;
import java.util.ArrayList;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import budget.util.BudgetProperties.DayOfWeek;
import budget.util.LocalDateAdapter;
@XmlRootElement(name = "budget")
public class BudgetNoFX {
// default to Sunday
DayOfWeek startOfWeek = DayOfWeek.SUNDAY;
// default to now
LocalDate startDate = LocalDate.now();
// number of days beyond week end to include in list of due bills
// default to 3
Integer daysBeyondWeek = new Integer(3);
Integer numberOfWeeks = new Integer(0);
ArrayList<CategoryNoFX> categories = new ArrayList<CategoryNoFX>();
// startOfWeek
public DayOfWeek getStartOfWeek() {
return this.startOfWeek;
}
public void setStartOfWeek(DayOfWeek startOfWeek) {
this.startOfWeek = startOfWeek;
}
// startDate
@XmlJavaTypeAdapter(LocalDateAdapter.class)
public LocalDate getStartDate() {
return this.startDate;
}
public void setStartDate(LocalDate startDate){
this.startDate = startDate;
}
// daysBeyondWeek
public Integer getDaysBeyondWeek() {
return this.daysBeyondWeek;
}
public void setDaysBeyondWeek(Integer daysBeyondWeek) {
this.daysBeyondWeek = daysBeyondWeek;
}
// numberOFWeeks
public Integer getNumberOfWeeks() {
return this.numberOfWeeks;
}
public void setNumberOfWeeks(Integer numberOfWeeks) {
this.numberOfWeeks = numberOfWeeks;
}
// categories
public ArrayList<CategoryNoFX> getCategories () {
return categories;
}
public void setCategories(ArrayList<CategoryNoFX> categories) {
this.categories = categories;
}
}
package budget.model;
import java.time.LocalDate;
import java.util.ArrayList;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import budget.util.BudgetProperties.RepeatFrequency;
import budget.util.LocalDateAdapter;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
public class CategoryNoFX {
public static final String ROOT_CATEGORY = "ROOT";
String name = new String("");
ArrayList<CategoryNoFX> children = new ArrayList<CategoryNoFX>();
String comments = new String("");
// default to monthly
RepeatFrequency repeatFrequency = RepeatFrequency.MONTHLY;
LocalDate dueDate = LocalDate.now();
Double budgetAmount = new Double(0);
Double actualAmount = new Double(0);
// name
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
// children
public ArrayList<CategoryNoFX> getChildren() {
return children;
}
public void setChildren(ArrayList<CategoryNoFX> children) {
this.children = children;
}
public void addChild(CategoryNoFX category) {
this.children.add(category);
}
// isParent
public Boolean isParent() {
if (children == null || children.isEmpty() || children.size() == 0) {
return false;
} else {
return true;
}
}
// comments
public String getComments() {
return comments;
}
public void setComments(String comments) {
this.comments = comments;
}
// repeatFrequency
public RepeatFrequency getRepeatFrequency() {
return this.repeatFrequency;
}
public void setRepeatFrequency(RepeatFrequency repeatFrequency) {
this.repeatFrequency = repeatFrequency;
}
// dueDate
@XmlJavaTypeAdapter(LocalDateAdapter.class)
public LocalDate getDueDate() {
return this.dueDate;
}
public void setDueDate(LocalDate dueDate) {
this.dueDate = dueDate;
}
// budgetAmount
public Double getBudgetAmount() {
return this.budgetAmount;
}
public void setBudgetAmount(Double budgetAmount) {
this.budgetAmount = budgetAmount;
}
// actualAmount
public Double getActualAmount() {
return this.actualAmount;
}
public void setActualAmount(Double actualAmount) {
this.actualAmount = actualAmount;
}
}
我更新了saveBudgetData函数以使用新的预算类
public void saveBudgetData(BudgetNoFX budget) {
File file = new File(path + BUDGET_FILE);
try {
JAXBContext context = JAXBContext
.newInstance(BudgetNoFX.class);
Marshaller m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
// Marshalling and saving XML to the file.
m.marshal(budget, file);
} catch (Exception e) { // catches ANY exception
logger.error("exception: ", e);
Alert alert = new Alert(AlertType.ERROR);
alert.setTitle("Error");
alert.setHeaderText("Could not save data");
alert.setContentText("Could not save data to file:\n" + file.getPath());
alert.showAndWait();
}
}
答案 0 :(得分:1)
我有点尴尬。我知道你必须小心递归,这是我的问题。
在构建ui之前,我对一些值进行了硬编码 - 创建了预算并添加了一些类别。我应该发布该代码。我把其中一个类别设置为自己的孩子。
Category food = new Category();
food.setName("Food");
categories.add(food);
Category groceries = new Category();
groceries.setBudgetAmount(new Double(120));
groceries.setName("Groceries");
// groceries.setParentCategory("Food");
groceries.setRepeatFrequency(RepeatFrequency.WEEKLY);
food.addChild(food); <-- problem line
我将违规行修复为
food.addChild(groceries);
它开始工作了。
我通过将保存功能注释到XML并将预算对象写到屏幕上来找到它。
我最近阅读了本教程:http://code.makery.ch/library/javafx-8-tutorial/并构建了另一个简单的应用程序。这是LocalDateAdapter类的来源。在第5部分中,他解释了jaxb和列表。我做了一些代码更改以更好地处理我的列表,我得到了我很满意的xml输出。
感谢您花时间查看我的代码并帮助我。 如果我做到这一点,也许我会将应用程序/代码发布到互联网上。我以前从未这样做过,也不知道最好的地方。
再次,谢谢。 克里斯