XML Parser in Java
Reading and updating an XML file with Java's built-in DOM parser and the XPath API: change attributes, update values, and add and remove elements.
Archive note I wrote this in 2020 on my old blog. It moved here in 2026 with the code and diagrams redone; library versions and APIs may have changed since. Original post.
Contents
In this article we’ll read and modify XML data in Java. There are plenty of built-in and third-party APIs for this; we’ll use the built-in DOM parser and the XPath API.
The input
This is the XML we’ll update:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Jobs>
<Job id="0">
<position>Data Analyst</position>
<skill>Python</skill>
<vacancies>3</vacancies>
</Job>
<Job id="2">
<position>Developer</position>
<skill>CSS</skill>
<vacancies>8</vacancies>
</Job>
<Job id="3">
<position>Developer</position>
<skill>SpringBoot</skill>
<vacancies>1</vacancies>
</Job>
</Jobs>The output we want
We’ll change the first job’s id from 0 to 1, add one to every vacancies count, and add a salary element to every job except the first:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Jobs>
<Job id="1">
<position>Data Analyst</position>
<skill>Python</skill>
<vacancies>4</vacancies>
</Job>
<Job id="2">
<position>Developer</position>
<skill>CSS</skill>
<vacancies>9</vacancies>
<salary>100K</salary>
</Job>
<Job id="3">
<position>Developer</position>
<skill>SpringBoot</skill>
<vacancies>2</vacancies>
<salary>100K</salary>
</Job>
</Jobs>The code
import java.io.File;
import java.io.IOException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
public class AppMain {
public static void main(String[] args) {
String sourceFilePath = "D:\\Developers.xml";
String destinationFilePath = "D:\\Developers_updated.xml";
File xmlFile = new File(sourceFilePath);
try {
DocumentBuilderFactory documentFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentFactory.newDocumentBuilder();
Document document = documentBuilder.parse(xmlFile);
// Remove unwanted whitespace and merge adjacent text nodes
document.getDocumentElement().normalize();
// Use the XPath API to query the document
XPath xPath = XPathFactory.newInstance().newXPath();
// Every <Job> node under the root <Jobs> node
NodeList jobList = (NodeList) xPath.compile("/Jobs/Job")
.evaluate(document, XPathConstants.NODESET);
// Change the id attribute from 0 to 1
for (int i = 0; i < jobList.getLength(); i++) {
Node idAttribute = jobList.item(i).getAttributes().getNamedItem("id");
if (idAttribute.getTextContent().equalsIgnoreCase("0")) {
idAttribute.setTextContent("1");
}
}
// Add one to every vacancies count
NodeList vacancies = (NodeList) xPath.compile("/Jobs/Job/vacancies")
.evaluate(document, XPathConstants.NODESET);
for (int i = 0; i < vacancies.getLength(); i++) {
Node vacancy = vacancies.item(i).getFirstChild();
int newVacancy = Integer.parseInt(vacancy.getNodeValue()) + 1;
vacancy.setTextContent(String.valueOf(newVacancy));
}
// Add a new <salary> element to every job
for (int i = 0; i < jobList.getLength(); i++) {
Element salary = document.createElement("salary");
salary.appendChild(document.createTextNode("100K"));
jobList.item(i).appendChild(salary);
}
// Remove <salary> from the first job
NodeList childNodes = jobList.item(0).getChildNodes();
for (int j = 0; j < childNodes.getLength(); j++) {
if (childNodes.item(j).getNodeName().equalsIgnoreCase("salary")) {
// jobList.item(0) is the <Job> node; pass it the child to remove
jobList.item(0).removeChild(childNodes.item(j));
}
}
// Write the document back out as an XML file
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty(OutputKeys.METHOD, "xml");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
DOMSource source = new DOMSource(document);
StreamResult result = new StreamResult(new File(destinationFilePath));
transformer.transform(source, result);
System.out.println("XML file update completed");
} catch (SAXException | ParserConfigurationException | IOException
| TransformerException | XPathExpressionException e) {
e.printStackTrace();
}
}
}How it works
The DOM parser loads the whole file into memory as a tree, and XPath lets you query that tree with path expressions. XPath is powerful: you can select nodes not only by element or attribute name but also by their content, for example /Jobs/Job[skill='CSS'].
Because the DOM parser loads the entire document into memory, it isn’t a good fit for very large files. For those, look at streaming parsers such as SAX or StAX, or at JAXB for mapping XML to Java objects. Each has its own trade-offs.