Я пишу простую программу бюджетирования, которая имеет бюджетный класс с массивом классов категорий. Каждый класс категории может иметь классы дочерних категорий. Когда я пытаюсь сохранить данные в файл XML с помощью JAXB, я получаю сообщение об ошибке com.sun.istack.internal.SAXException2: В графе объектов обнаружен цикл. Это приведет к бесконечно глубокому XMLJAXB Это приведет к бесконечно глубокому XML
Я искал ошибку и вижу, что она вызвана родительским отношением дочерних элементов, где родительский указатель указывает на дочерний элемент, а дочерний элемент ссылается на родителя. Большинство ответов - использовать @XMLTransient.
Моя проблема заключается в том, что мой класс категории не ссылается ни на родителя бюджета, ни на родителя категории, если он существует.
Я новичок в 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;
}
}
Существует еще один класс, который обрабатывает сортировку. Функция в этом классе ниже
Я понимаю, что это рекурсивная взаимосвязь между категориями. Это то, о чем он жалуется? В моем поиске я не видел этого сценария.
Спасибо.
Подчищен Бюджет и Категория классы
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();
}
}
Попробуйте определить класс класса clea и не используйте поля javafx в it.it должно быть POJO – Cris
Я очистил бюджет и категорию, чтобы удалить javafx, и они просто POJO. У меня все еще такая же ошибка. –
Сообщение, пожалуйста, чистый код – Cris