Explanation
I'm making program that use a XML file to save some values to use as settings, and only need to read and modify the element values in the XML. The code is written in Java and I'm using JDOM 2.0.6.1 to manage the XML file, followed this tutorial.
The code works when overwrite any element in the XML, but just 1 time. If make multiple operations to modify any value, the program start appending a new tree with the changes, which should not happen and would be the problem.
The default XML:
<?xml version="1.0" encoding="UTF-8"?>
<settings>
<time>
<time-work>25</time-work>
<time-break>5</time-break>
<time-rest>15</time-rest>
<time-interval>3</time-interval>
</time>
<auto-start>
<start-work>true</start-work>
<start-break>true</start-break>
<start-rest>true</start-rest>
</auto-start>
</settings>
The Java code:
public class FileHandler {
private final Writer settingsWriter;
private final XMLOutputter xmlOutput;
private final Document settingsDoc;
private final Element settingsElement;
FileHandler() throws IOException, JDOMException {
Path settingsPath = Paths.get("res/settings/settings.xml");
File settingsFile = settingsPath.toFile();
boolean settingsExist = settingsFile.createNewFile();
settingsWriter = Channels.newWriter(
FileChannel.open(settingsPath, StandardOpenOption.WRITE),
StandardCharsets.UTF_8);
xmlOutput = new XMLOutputter();
xmlOutput.setFormat(Format.getPrettyFormat());
if (settingsExist) createSettings();
SAXBuilder saxBuilder = new SAXBuilder();
settingsDoc = saxBuilder.build(settingsFile);
settingsElement = settingsDoc.getRootElement();
setElementValue("time-work", "5");
setElementValue("time-work", "10");
setElementValue("time-work", "30");
}
public void setElementValue(String element, String newValue) throws IOException, JDOMException {
Element groupElement = settingsElement.getChild(element.startsWith("time")? "time":"auto-start"),
toChangeElement = groupElement.getChild(element);
toChangeElement.setText(newValue);
xmlOutput.output(settingsDoc, settingsWriter);
}
/*More code, irrelevant for the post*/
}
Examples
Example 1 (Works)
FileHandler() throws IOException, JDOMException {
Path settingsPath = Paths.get("res/settings/settings.xml");
File settingsFile = settingsPath.toFile();
boolean settingsExist = settingsFile.createNewFile();
settingsWriter = Channels.newWriter(
FileChannel.open(settingsPath, StandardOpenOption.WRITE),
StandardCharsets.UTF_8);
xmlOutput = new XMLOutputter();
xmlOutput.setFormat(Format.getPrettyFormat());
if (settingsExist) createSettings();
SAXBuilder saxBuilder = new SAXBuilder();
settingsDoc = saxBuilder.build(settingsFile);
settingsElement = settingsDoc.getRootElement();
setElementValue("time-work", "5");
}
<?xml version="1.0" encoding="UTF-8"?>
<settings>
<time>
<time-work>5</time-work>
<time-break>5</time-break>
<time-rest>15</time-rest>
<time-interval>3</time-interval>
</time>
<auto-start>
<start-work>true</start-work>
<start-break>true</start-break>
<start-rest>true</start-rest>
</auto-start>
</settings>
Example 2 (Works)
FileHandler() throws IOException, JDOMException {
Path settingsPath = Paths.get("res/settings/settings.xml");
File settingsFile = settingsPath.toFile();
boolean settingsExist = settingsFile.createNewFile();
settingsWriter = Channels.newWriter(
FileChannel.open(settingsPath, StandardOpenOption.WRITE),
StandardCharsets.UTF_8);
xmlOutput = new XMLOutputter();
xmlOutput.setFormat(Format.getPrettyFormat());
if (settingsExist) createSettings();
SAXBuilder saxBuilder = new SAXBuilder();
settingsDoc = saxBuilder.build(settingsFile);
settingsElement = settingsDoc.getRootElement();
setElementValue("start-rest", "2");
}
<?xml version="1.0" encoding="UTF-8"?>
<settings>
<time>
<time-work>25</time-work>
<time-break>5</time-break>
<time-rest>15</time-rest>
<time-interval>3</time-interval>
</time>
<auto-start>
<start-work>true</start-work>
<start-break>true</start-break>
<start-rest>2</start-rest>
</auto-start>
</settings>
Example 3 (Fail)
FileHandler() throws IOException, JDOMException {
Path settingsPath = Paths.get("res/settings/settings.xml");
File settingsFile = settingsPath.toFile();
boolean settingsExist = settingsFile.createNewFile();
settingsWriter = Channels.newWriter(
FileChannel.open(settingsPath, StandardOpenOption.WRITE),
StandardCharsets.UTF_8);
xmlOutput = new XMLOutputter();
xmlOutput.setFormat(Format.getPrettyFormat());
if (settingsExist) createSettings();
SAXBuilder saxBuilder = new SAXBuilder();
settingsDoc = saxBuilder.build(settingsFile);
settingsElement = settingsDoc.getRootElement();
setElementValue("time-work", "5");
setElementValue("time-work", "10");
setElementValue("time-work", "30");
}
<?xml version="1.0" encoding="UTF-8"?>
<settings>
<time>
<time-work>5</time-work>
<time-break>5</time-break>
<time-rest>15</time-rest>
<time-interval>3</time-interval>
</time>
<auto-start>
<start-work>true</start-work>
<start-break>true</start-break>
<start-rest>true</start-rest>
</auto-start>
</settings>
<?xml version="1.0" encoding="UTF-8"?>
<settings>
<time>
<time-work>10</time-work>
<time-break>5</time-break>
<time-rest>15</time-rest>
<time-interval>3</time-interval>
</time>
<auto-start>
<start-work>true</start-work>
<start-break>true</start-break>
<start-rest>true</start-rest>
</auto-start>
</settings>
<?xml version="1.0" encoding="UTF-8"?>
<settings>
<time>
<time-work>30</time-work>
<time-break>5</time-break>
<time-rest>15</time-rest>
<time-interval>3</time-interval>
</time>
<auto-start>
<start-work>true</start-work>
<start-break>true</start-break>
<start-rest>true</start-rest>
</auto-start>
</settings>
Example 4 (Fail)
FileHandler() throws IOException, JDOMException {
Path settingsPath = Paths.get("res/settings/settings.xml");
File settingsFile = settingsPath.toFile();
boolean settingsExist = settingsFile.createNewFile();
settingsWriter = Channels.newWriter(
FileChannel.open(settingsPath, StandardOpenOption.WRITE),
StandardCharsets.UTF_8);
xmlOutput = new XMLOutputter();
xmlOutput.setFormat(Format.getPrettyFormat());
if (settingsExist) createSettings();
SAXBuilder saxBuilder = new SAXBuilder();
settingsDoc = saxBuilder.build(settingsFile);
settingsElement = settingsDoc.getRootElement();
setElementValue("time-work", "5");
setElementValue("time-break", "10");
setElementValue("time-rest", "30");
}
<?xml version="1.0" encoding="UTF-8"?>
<settings>
<time>
<time-work>5</time-work>
<time-break>5</time-break>
<time-rest>15</time-rest>
<time-interval>3</time-interval>
</time>
<auto-start>
<start-work>true</start-work>
<start-break>true</start-break>
<start-rest>true</start-rest>
</auto-start>
</settings>
<?xml version="1.0" encoding="UTF-8"?>
<settings>
<time>
<time-work>5</time-work>
<time-break>10</time-break>
<time-rest>15</time-rest>
<time-interval>3</time-interval>
</time>
<auto-start>
<start-work>true</start-work>
<start-break>true</start-break>
<start-rest>true</start-rest>
</auto-start>
</settings>
<?xml version="1.0" encoding="UTF-8"?>
<settings>
<time>
<time-work>5</time-work>
<time-break>10</time-break>
<time-rest>30</time-rest>
<time-interval>3</time-interval>
</time>
<auto-start>
<start-work>true</start-work>
<start-break>true</start-break>
<start-rest>true</start-rest>
</auto-start>
</settings>
Example 5 (Fail)
FileHandler() throws IOException, JDOMException {
Path settingsPath = Paths.get("res/settings/settings.xml");
File settingsFile = settingsPath.toFile();
boolean settingsExist = settingsFile.createNewFile();
settingsWriter = Channels.newWriter(
FileChannel.open(settingsPath, StandardOpenOption.WRITE),
StandardCharsets.UTF_8);
xmlOutput = new XMLOutputter();
xmlOutput.setFormat(Format.getPrettyFormat());
if (settingsExist) createSettings();
SAXBuilder saxBuilder = new SAXBuilder();
settingsDoc = saxBuilder.build(settingsFile);
settingsElement = settingsDoc.getRootElement();
setElementValue("time-work", "5");
setElementValue("start-break", "false");
setElementValue("time-interval", "2");
}
<?xml version="1.0" encoding="UTF-8"?>
<settings>
<time>
<time-work>5</time-work>
<time-break>5</time-break>
<time-rest>15</time-rest>
<time-interval>3</time-interval>
</time>
<auto-start>
<start-work>true</start-work>
<start-break>true</start-break>
<start-rest>true</start-rest>
</auto-start>
</settings>
<?xml version="1.0" encoding="UTF-8"?>
<settings>
<time>
<time-work>5</time-work>
<time-break>5</time-break>
<time-rest>15</time-rest>
<time-interval>3</time-interval>
</time>
<auto-start>
<start-work>true</start-work>
<start-break>false</start-break>
<start-rest>true</start-rest>
</auto-start>
</settings>
<?xml version="1.0" encoding="UTF-8"?>
<settings>
<time>
<time-work>5</time-work>
<time-break>5</time-break>
<time-rest>15</time-rest>
<time-interval>2</time-interval>
</time>
<auto-start>
<start-work>true</start-work>
<start-break>false</start-break>
<start-rest>true</start-rest>
</auto-start>
</settings>
What I tried
Tried to close the writer and use re-building the "Document settingsDoc" inside the function.
Everything I tried ended in the same result, appending a new XML tree after overwriting just 1.