Migration Guide
This guide helps you migrate from other XML libraries to DomTrip. We cover the most common migration scenarios and provide side-by-side examples.
From DOM4J
DOM4J is one of the most popular XML libraries for Java. Here's how to migrate common patterns:
Document Loading
// DOM4J
// SAXReader reader = new SAXReader();
// Document document = reader.read(new StringReader(xml));
// DomTrip
String xml = createTestXml("root");
Document document = Document.of(xml);
Editor editor = new Editor(document);
Element Navigation
// DOM4J
// Element root = document.getDocumentElement();
// Element child = root.element("child");
// List<Element> children = root.elements("item");
// DomTrip
String xml = createTestXml("root");
Document document = Document.of(xml);
Editor editor = new Editor(document);
Element root = editor.root();
Optional<Element> child = root.child("child");
Stream<Element> children = root.children("item");
Adding Elements
// DOM4J
// Element parent = root.element("dependencies");
// Element dependency = parent.addElement("dependency");
// dependency.addElement("groupId").setText("junit");
// dependency.addElement("artifactId").setText("junit");
// DomTrip
String xml = createMavenPomXml();
Document document = Document.of(xml);
Editor editor = new Editor(document);
Element parent = editor.root().descendant("dependencies").orElseThrow();
Element dependency = editor.addElement(parent, "dependency");
editor.addElement(dependency, "groupId", "junit");
editor.addElement(dependency, "artifactId", "junit");
Attribute Handling
// DOM4J
// element.addAttribute("scope", "test");
// String scope = element.attributeValue("scope");
// DomTrip
String xml = createTestXml("element");
Document document = Document.of(xml);
Editor editor = new Editor(document);
Element element = editor.root();
editor.setAttribute(element, "scope", "test");
String scope = element.attribute("scope");
Serialization
// DOM4J
// OutputFormat format = OutputFormat.createPrettyPrint();
// XMLWriter writer = new XMLWriter(outputStream, format);
// writer.write(document);
// DomTrip
String xml = createTestXml("root");
Document document = Document.of(xml);
Editor editor = new Editor(document);
String result = editor.toXml(); // Preserves original formatting
String prettyXml = editor.toXml(DomTripConfig.prettyPrint());
From JDOM
JDOM has a simpler API than DOM4J but similar concepts:
Document Loading
// JDOM
// SAXBuilder builder = new SAXBuilder();
// Document document = builder.build(new StringReader(xml));
// DomTrip
String xml = createTestXml("root");
Document document = Document.of(xml);
Editor editor = new Editor(document);
Element Operations
// JDOM
// Element root = document.getDocumentElement();
// Element child = root.getChild("child");
// List<Element> children = root.getChildren("item");
//
// Add new element
// Element newElement = new Element("newChild");
// newElement.setText("content");
// root.addContent(newElement);
// DomTrip
String xml = createTestXml("root");
Document document = Document.of(xml);
Editor editor = new Editor(document);
Element root = editor.root();
Optional<Element> child = root.child("child");
Stream<Element> children = root.children("item");
// Add new element
Element newElement = editor.addElement(root, "newChild", "content");
Text Content
// JDOM
// element.setText("new content");
// String content = element.getText();
// DomTrip
String xml = createTestXml("element");
Document document = Document.of(xml);
Editor editor = new Editor(document);
Element element = editor.root();
editor.setTextContent(element, "new content");
String content = element.textContent();
From Java DOM
The built-in Java DOM API is verbose but powerful:
Document Loading
// Java DOM
// DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// DocumentBuilder builder = factory.newDocumentBuilder();
// Document document = builder.parse(new InputSource(new StringReader(xml)));
// DomTrip
String xml = createTestXml("root");
Document document = Document.of(xml);
Editor editor = new Editor(document);
Element Navigation
// Java DOM
// Element root = document.getDocumentElement();
// NodeList children = root.getElementsByTagName("child");
// Element child = (Element) children.item(0);
// DomTrip
String xml = createTestXml("root");
Document document = Document.of(xml);
Editor editor = new Editor(document);
Element root = editor.root();
Optional<Element> child = root.child("child");
Creating Elements
// Java DOM
// Element newElement = document.createElement("newChild");
// newElement.setTextContent("content");
// parent.appendChild(newElement);
// DomTrip
String xml = createTestXml("parent");
Document document = Document.of(xml);
Editor editor = new Editor(document);
Element parent = editor.root();
Element newElement = editor.addElement(parent, "newChild", "content");
Attributes
// Java DOM
// element.setAttribute("scope", "test");
// String scope = element.getAttribute("scope");
// DomTrip
String xml = createTestXml("element");
Document document = Document.of(xml);
Editor editor = new Editor(document);
Element element = editor.root();
editor.setAttribute(element, "scope", "test");
String scope = element.attribute("scope");
From Jackson XML
Jackson XML is primarily for object mapping, but here are equivalent operations:
Simple Parsing
// Jackson XML
// XmlMapper mapper = new XmlMapper();
// JsonNode root = mapper.readTree(xml);
// JsonNode child = root.get("child");
// DomTrip
String xml = createTestXml("root");
Document document = Document.of(xml);
Editor editor = new Editor(document);
Element root = editor.root();
Optional<Element> child = root.child("child");
Object Mapping vs Manual Construction
// Jackson XML (object mapping)
// @JacksonXmlRootElement(localName = "dependency")
// public class Dependency {
// public String groupId;
// public String artifactId;
// public String version;
// }
//
// Dependency dep = new Dependency();
// dep.groupId = "junit";
// dep.artifactId = "junit";
// dep.version = "4.13.2";
//
// String xml = mapper.writeValueAsString(dep);
// DomTrip (manual construction)
Element dependency = Element.of("dependency");
dependency.addNode(Element.text("groupId", "junit"));
dependency.addNode(Element.text("artifactId", "junit"));
dependency.addNode(Element.text("version", "4.13.2"));
String xml = dependency.toXml();
Common Migration Patterns
1. Error Handling
// Old libraries (various exceptions)
// try {
// // DOM4J
// Document doc = reader.read(xml);
// } catch (DocumentException e) {
// // Handle parsing error
// }
//
// try {
// // JDOM
// Document doc = builder.build(xml);
// } catch (JDOMException | IOException e) {
// // Handle parsing error
// }
// DomTrip (consistent exceptions)
try {
String xml = createTestXml("root");
Document document = Document.of(xml);
Editor editor = new Editor(document);
} catch (Exception e) {
// Handle parsing error
System.err.println("Failed to parse: " + e.getMessage());
}
2. Namespace Handling
// DOM4J
// Namespace ns = Namespace.get("soap", "http://schemas.xmlsoap.org/soap/envelope/");
// Element envelope = root.element(QName.get("Envelope", ns));
// DomTrip
String xml = createSoapXml();
Document document = Document.of(xml);
Editor editor = new Editor(document);
Element root = editor.root();
// Check if root itself is the Envelope element
assertEquals("Envelope", root.localName());
assertEquals("http://schemas.xmlsoap.org/soap/envelope/", root.namespaceURI());
3. XPath Queries
// DOM4J (XPath support)
// List<Element> nodes = document.selectNodes("//dependency[scope='test']");
// DomTrip (Stream-based filtering)
String xml = createMavenPomXml();
Document document = Document.of(xml);
Editor editor = new Editor(document);
Element root = editor.root();
Stream<Element> nodes = root.descendants()
.filter(el -> "dependency".equals(el.name()))
.filter(el -> "test".equals(el.attribute("scope")));
Breaking Changes in Recent Versions
Whitespace API Simplification (v0.1.1+)
The whitespace handling API has been simplified for better maintainability:
Removed Methods:
Node.followingWhitespace()
andNode.followingWhitespace(String)
Element.innerFollowingWhitespace()
andElement.innerFollowingWhitespace(String)
Migration Strategy:
// OLD: Setting whitespace after a node
node.followingWhitespace("\n ");
// NEW: Set whitespace before the next node instead
nextNode.precedingWhitespace("\n ");
// OLD: Setting whitespace after opening tag
element.innerFollowingWhitespace("\n ");
// NEW: Set whitespace before first child instead
firstChild.precedingWhitespace("\n ");
Rationale: The simplified model eliminates redundant whitespace storage where the same whitespace was stored in multiple places. This reduces memory usage and eliminates synchronization issues.
Migration Checklist
Before Migration
- [ ] Identify all XML processing code in your application
- [ ] Document current XML formatting requirements
- [ ] Create test cases for existing functionality
- [ ] Note any XPath usage (DomTrip doesn't support XPath)
During Migration
- [ ] Replace library imports
- [ ] Update document loading code
- [ ] Convert element navigation to DomTrip patterns
- [ ] Update attribute handling
- [ ] Replace serialization code
- [ ] Handle namespace operations
- [ ] Update exception handling
After Migration
- [ ] Run all existing tests
- [ ] Verify XML output formatting
- [ ] Check performance impact
- [ ] Update documentation
- [ ] Train team on new API patterns
Performance Considerations
Memory Usage
// Old approach (minimal memory)
// Document doc = parser.parse(xml);
// Memory: ~1x base size
// DomTrip (includes formatting metadata)
String xml = createTestXml("root");
Document document = Document.of(xml);
Editor editor = new Editor(document);
// Memory: ~1.3x base size
Processing Speed
- Parsing: DomTrip is ~15% slower due to metadata collection
- Navigation: Similar performance to other DOM libraries
- Serialization: Faster for unmodified content, slower for heavily modified content
Gradual Migration Strategy
Phase 1: New Code
Start using DomTrip for all new XML processing code:
// New features use DomTrip
// public void addDependency(String pomPath, Dependency dep) {
// Document doc = Document.of(Path.of(pomPath));
// Editor editor = new Editor(doc);
// // ... DomTrip operations
// }
// Example implementation
String pomXml = createMavenPomXml();
Document doc = Document.of(pomXml);
Editor editor = new Editor(doc);
// DomTrip operations here
Phase 2: Critical Paths
Migrate code that requires formatting preservation:
// Configuration file editing (formatting critical)
// public void updateConfig(String configPath, Map<String, String> updates) {
// // Migrate to DomTrip for lossless editing
// Document doc = Document.of(Path.of(configPath));
// Editor editor = new Editor(doc);
// // ...
// }
// Example implementation
String configXml = createConfigXml();
Document doc = Document.of(configXml);
Editor editor = new Editor(doc);
// Lossless editing operations here
Phase 3: Complete Migration
Replace remaining XML processing code:
// Data extraction (formatting less critical)
// public List<String> extractValues(String xml) {
// // Can migrate to DomTrip or keep existing approach
// // based on requirements
// }
// Example implementation
String xml = createTestXml("root");
Document document = Document.of(xml);
Editor editor = new Editor(document);
// Data extraction operations here
Getting Help
If you encounter issues during migration: