JAXB这将导致无限深度的XML

时间:2016-09-30 22:06:43

标签: java xml jaxb

我正在编写一个简单的预算程序,该程序具有带有类别类数组的预算类。每个类别类都可以有子类别。当我尝试使用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();
        }
    }

1 个答案:

答案 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输出。

感谢您花时间查看我的代码并帮助我。 如果我做到这一点,也许我会将应用程序/代码发布到互联网上。我以前从未这样做过,也不知道最好的地方。

再次,谢谢。 克里斯