java - JAXB 这会导致无限深的XML

标签 java xml jaxb

我正在编写一个简单的预算程序,其中有一个预算类和一系列类别类。每个类别类可以有子类别类。当我尝试使用 JAXB 将数据保存到 XML 文件时,出现错误 com.sun.istack.internal.SAXException2:在对象图中检测到循环。这将导致无限深的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;
}

}

还有另一个类处理编码。该类的函数如下

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

最佳答案

我有点尴尬。我知道你必须小心递归,这就是我的问题。

在构建用户界面之前,我对一些值进行了硬编码 - 创建了预算并添加了一些类别。我应该发布该代码。我小时候就将其中一个类别设置为它自己。

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

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

再次感谢。 克里斯

关于java - JAXB 这会导致无限深的XML,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39800730/

相关文章:

python - 使用 python 将 xml 附加到 xml。我有两个需要合并的 xml 文件。所以无论如何我可以合并这两个文件

java - JAXB 将复杂类型替换为原始类型

java - 如何处理数字格式异常并将 int/double 变量替换为默认值 0/0.0?

java - 将序列化 java 对象保存到 Db 时出现问题

xml - xml 声明的大写

java - 使用 JAXB 读取和写入 XML 文件时处理 org.xml.sax.SAXParseException

xml - 用于生成 xml 并返回数组/JAXB 对象列表的 RESTEasy 服务,提供空集合

java - Java 中未按要求获取用户输入

Java:如何从字符串和整数计算答案

Java xml 解析器传递错误