In the process of serializing to XML file, I'm encountering an infinite loop issue. I'm using java.beans, and through XMLEncoder, I'm attempting to serialize three classes: Category, Contact, and Event. Unfortunately, I cannot create an XML file due to the fact that the Contact class contains a field List events, and similarly, the Event class has a field List contacts, creating a mutual reference between them. The problem is that XMLEncoder, while trying to serialize everything, falls into the reference loop between the Contact and Event classes, resulting in an error as it may continue indefinitely.
Serializer:
public class XMLSerializer
{
public void encode(List<Category> categories, List<Event> events, List<Contact> contacts)
{
try (XMLEncoder xmlEncoder = new XMLEncoder(new BufferedOutputStream(new FileOutputStream("data/xml_files/data.xml"))))
{
xmlEncoder.setPersistenceDelegate(LocalDateTime.class, new LocalDateTimePersistenceDelegate());
xmlEncoder.setPersistenceDelegate(LocalTime.class, new LocalTimePersistenceDelegate());
List<Object> mergedList = new ArrayList<>();
mergedList.addAll(categories);
mergedList.addAll(events);
mergedList.addAll(contacts);
xmlEncoder.writeObject(mergedList);
}
catch (IOException e)
{
e.printStackTrace();
}
}
public class LocalDateTimePersistenceDelegate extends DefaultPersistenceDelegate
{
@Override
protected Expression instantiate(Object oldInstance, Encoder out)
{
LocalDateTime ldt = (LocalDateTime) oldInstance;
return new Expression(ldt, oldInstance.getClass(), "parse", new Object[] { ldt.toString() });
}
}
public class LocalTimePersistenceDelegate extends DefaultPersistenceDelegate
{
@Override
protected Expression instantiate(Object oldInstance, Encoder out)
{
LocalTime lt = (LocalTime) oldInstance;
return new Expression(lt, oldInstance.getClass(), "parse", new Object[] { lt.toString() });
}
}
}
Models:
public class Category implements Comparable<Category>
{
private int id;
private String name;
private String colorHex;
public Category()
{
}
@Override
public String toString()
{
return name;
}
public int getId()
{
return id;
}
public void setId(int id)
{
this.id = id;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public String getColorHex()
{
return colorHex;
}
public void setColorHex(String colorHex)
{
this.colorHex = colorHex;
}
@Override
public int compareTo(Category o)
{
return this.name.compareTo(o.getName());
}
}
public class Event implements Comparable<Event>
{
private int id;
private String name;
private LocalDateTime date;
private LocalTime notifyOffset;
private String location;
private String description;
private Category category;
private List<Contact> contacts = new ArrayList<Contact>();
public Event()
{
}
@Override
public String toString()
{
String locationStr = location.isEmpty() ? "No location" : location;
String descriptionStr = description.isEmpty() ? "No description" : description;
String categoryName = category != null ? category.getName() : "No category";
return String.format("%s | %s | %s%n%s%n%s", name, getFormattedDate(), locationStr, descriptionStr, categoryName);
}
public int getId()
{
return id;
}
public void setId(int id)
{
this.id = id;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public LocalDateTime getDate()
{
return date;
}
public void setDate(LocalDateTime date)
{
this.date = date;
}
public LocalTime getNotifyOffset()
{
return notifyOffset;
}
public void setNotifyOffset(LocalTime notifyOffset)
{
this.notifyOffset = notifyOffset;
}
public String getLocation()
{
return location;
}
public void setLocation(String location)
{
this.location = location;
}
public String getDescription()
{
return description;
}
public void setDescription(String description)
{
this.description = description;
}
public Category getCategory()
{
return category;
}
public void setCategory(Category category)
{
this.category = category;
}
public List<Contact> getContacts()
{
return Collections.unmodifiableList(contacts);
}
public void setContacts(List<Contact> contacts)
{
this.contacts = contacts;
for (Contact contact : contacts)
{
contact.addEvent(this);
}
}
public void addContact(Contact contact)
{
if (!this.contacts.contains(contact))
{
this.contacts.add(contact);
contact.addEvent(this);
}
}
public void removeContact(Contact contact)
{
if (this.contacts.contains(contact))
{
this.contacts.remove(contact);
contact.removeEvent(this);
}
}
public String getFormattedDate()
{
return this.date.format(DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm"));
}
public String getFormattedDateWithOffset()
{
LocalDateTime offsetDateTime = this.date.minusHours(notifyOffset.getHour()).minusMinutes(notifyOffset.getMinute());
return offsetDateTime.format(DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm"));
}
public LocalDateTime getDateWithOffset()
{
LocalDateTime offsetDateTime = this.date.minusHours(notifyOffset.getHour()).minusMinutes(notifyOffset.getMinute());
return offsetDateTime;
}
@Override
public int compareTo(Event o)
{
return this.date.compareTo(o.getDate());
}
}
public class Contact implements Comparable<Contact>
{
private int id;
private String firstName;
private String lastName;
private String phoneNumber;
private List<Event> events = new ArrayList<Event>();
public Contact()
{
}
@Override
public String toString()
{
return String.format("%s %s | %s", firstName, lastName, phoneNumber);
}
public int getId()
{
return id;
}
public void setId(int id)
{
this.id = id;
}
public String getFirstName()
{
return firstName;
}
public void setFirstName(String firstName)
{
this.firstName = firstName;
}
public String getLastName()
{
return lastName;
}
public void setLastName(String lastName)
{
this.lastName = lastName;
}
public String getPhoneNumber()
{
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber)
{
this.phoneNumber = phoneNumber;
}
public List<Event> getEvents()
{
return Collections.unmodifiableList(events);
}
public void setEvents(List<Event> events)
{
this.events = events;
for (Event event : events)
{
event.addContact(this);
}
}
public void addEvent(Event event)
{
if (!this.events.contains(event))
{
this.events.add(event);
event.addContact(this);
}
}
public void removeEvent(Event event)
{
if (this.events.contains(event))
{
this.events.remove(event);
event.removeContact(this);
}
}
@Override
public int compareTo(Contact o)
{
return this.firstName.compareTo(o.getFirstName());
}
}