2016-09-30 7 views
0

Я пишу простую программу бюджетирования, которая имеет бюджетный класс с массивом классов категорий. Каждый класс категории может иметь классы дочерних категорий. Когда я пытаюсь сохранить данные в файл 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(); 
     } 
    } 
+0

Попробуйте определить класс класса clea и не используйте поля javafx в it.it должно быть POJO – Cris

+0

Я очистил бюджет и категорию, чтобы удалить javafx, и они просто POJO. У меня все еще такая же ошибка. –

+0

Сообщение, пожалуйста, чистый код – Cris

ответ

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

он начал работать.

Я нашел его, комментируя функцию save funciton для XML и вместо этого выписал свой бюджетный объект на экран.

Я недавно прочитал этот учебник: http://code.makery.ch/library/javafx-8-tutorial/ и создал еще одно простое приложение. Отсюда и появился класс LocalDateAdapter. В части 5 он объясняет о jaxb и списках. Я сделал некоторые изменения кода, чтобы лучше обрабатывать мои списки, и я получаю XML-результат, которым я доволен.

Спасибо, что нашли время посмотреть мой код и помочь мне. Если я это сделаю, возможно, я отправлю приложение/код в Интернет. Я никогда не делал этого раньше и не знаю лучшего места.

Снова, спасибо. Chris