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() and Node.followingWhitespace(String)
  • Element.innerFollowingWhitespace() and Element.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: