Lossless XML Editing for Java
DomTrip is a Java library for lossless XML editing that preserves every detail of your XML documents during round-trip operations. Perfect for configuration file editing, document transformation, and any scenario where maintaining original formatting is crucial.
- π Perfect Round-Trip: Preserves comments, whitespace, entity encoding, attribute quote styles, and formatting
- π Easy Editing: Make changes while keeping original formatting intact
- π Modern API: Built for Java 17+ with fluent builders, Stream-based navigation, and type-safe configuration
- π Namespace Aware: Comprehensive XML namespace support with resolution and context management
- β‘ Performance: Minimal-change serialization - only modified sections are reformatted
Add DomTrip to your Maven project:
<dependency>
<groupId>eu.maveniverse.maven.domtrip</groupId>
<artifactId>domtrip-maven</artifactId>
<version>0.2.0</version>
</dependency>
// Load and edit XML while preserving formatting
Editor editor = new Editor(Document.of(xmlString));
// Make targeted changes
Element version = editor.root().descendant("version").orElseThrow();
editor.setTextContent(version, "2.0.0");
// Add new elements with automatic formatting
Element dependencies = editor.root().descendant("dependencies").orElseThrow();
Element newDep = editor.addElement(dependencies, "dependency");
editor.addElement(newDep, "groupId", "junit");
editor.addElement(newDep, "artifactId", "junit");
// Get result with preserved formatting
String result = editor.toXml();
- Comments: Multi-line comments, inline comments, document comments
- Whitespace: Exact spacing, indentation, and line breaks
- Entities: Custom entity encoding (
<
,&
,"
) - Attributes: Quote styles (single vs double quotes), ordering, spacing
- Declarations: XML declarations, processing instructions, DTDs
- Fluent Builders:
Element.builder("dependency").withAttribute("scope", "test").build()
- Stream Navigation:
root.descendants().filter(e -> "dependency".equals(e.getName()))
- Optional Safety:
element.findChild("version").ifPresent(v -> ...)
- Type Safety: Compile-time prevention of invalid operations
- Configuration: Preset configurations for different use cases
- Namespace-Aware Navigation: Find elements by namespace URI and local name
- Context Management: Automatic namespace resolution and prefix handling
- Builder Integration: Create namespaced elements with fluent builders
- Preservation: Maintains namespace declarations and prefixes
- Getting Started - Installation and quick start guide
- Core Features - Lossless parsing, formatting preservation, namespaces
- API Reference - Complete API documentation
- Examples - Real-world usage examples
- Library Comparison - How DomTrip compares to other XML libraries
- π Test Coverage: 100% (59/59 tests passing)
- π XML Conformance: Full XML 1.0 specification compliance
- β‘ Performance: Optimized for both memory usage and processing speed
- π‘οΈ Type Safety: Compile-time prevention of invalid XML operations
- π Documentation: Comprehensive documentation with live examples
Create XML elements with convenient factory methods:
// Simple elements
Element version = Element.text("version", "1.0.0");
Element properties = Element.of("properties");
Element br = Element.selfClosing("br");
// Elements with attributes
Map<String, String> attrs = Map.of("scope", "test", "optional", "true");
Element dependency = Element.withAttributes("dependency", attrs);
// Namespaced elements
QName soapEnvelope = QName.of("http://schemas.xmlsoap.org/soap/envelope/", "Envelope", "soap");
Element nsElement = Element.of(soapEnvelope);
Element defaultNs = Element.of(QName.of("http://example.com/ns", "item"));
// Documents
Document doc = Document.of().root(Element.of("project"));
Document withDecl = Document.withXmlDeclaration("1.0", "UTF-8");
Create complex XML structures with fluent APIs:
Element dependency = Element.of("dependency")
.attribute("scope", "test");
dependency.addNode(Element.text("groupId", "junit"));
dependency.addNode(Element.text("artifactId", "junit"));
dependency.addNode(Element.text("version", "4.13.2"));
Navigate XML documents with Java Streams:
// Find all test dependencies
root.descendants()
.filter(e -> "dependency".equals(e.name()))
.filter(e -> "test".equals(e.attribute("scope")))
.forEach(dep -> System.out.println(dep.child("artifactId").map(Element::textContent).orElse("")));
Work with XML namespaces naturally:
// Find elements by namespace URI and local name
QName soapBodyQName = QName.of("http://schemas.xmlsoap.org/soap/envelope/", "Body");
Optional<Element> soapBody = root.child(soapBodyQName);
// Create namespaced elements
Element element = Element.of("item")
.namespaceDeclaration("ex", "http://example.com/ns")
.namespaceDeclaration(null, "http://example.com/default");
Customize DomTrip behavior for different use cases:
// Strict preservation (default)
DomTripConfig strict = DomTripConfig.defaults();
// Pretty printing for clean output
DomTripConfig pretty = DomTripConfig.prettyPrint()
.withIndentString(" ")
.withDefaultQuoteStyle(QuoteStyle.DOUBLE);
Editor editor = new Editor(Document.of(xml), pretty);
DomTrip uses a clean, type-safe architecture that enforces XML structure rules:
Node (abstract base)
βββ ContainerNode (abstract)
β βββ Document (root container)
β βββ Element (XML elements)
βββ Leaf Nodes
βββ Text (text content, CDATA)
βββ Comment (XML comments)
βββ ProcessingInstruction (PIs)
Editor
- High-level editing interface with formatting preservationParser
- XML parsing engine that captures all formatting metadataSerializer
- Minimal-change XML output generationDomTripConfig
- Configuration management with presets
- π Preservation First - Original formatting preserved unless explicitly modified
- π Metadata Tracking - Each node tracks modification state and formatting
- β‘ Minimal Changes - Only modified sections are reformatted
- π‘οΈ Type Safety - Compile-time prevention of invalid operations
DomTrip excels in scenarios where preserving original formatting is crucial:
- π Configuration Files - Update Maven POMs, Spring configs, web.xml while preserving comments and formatting
- π Document Transformation - Transform XML documents while maintaining original structure and style
- π οΈ XML Editing Tools - Build XML editors and IDEs that respect user formatting preferences
- π Template Processing - Process XML templates while preserving formatting for human readability
- π§ Build Tools - Modify build files programmatically without disrupting team formatting standards
Feature | DomTrip | DOM4J | JDOM | Jackson XML |
---|---|---|---|---|
Lossless Round-Trip | β Perfect | β No | β No | β No |
Comment Preservation | β Yes | β Yes | β Yes | β No |
Between-Element Whitespace | β Exact | β Yes* | β No | |
In-Element Whitespace | β Exact | β No | β No | |
Quote Style Preservation | β Yes | β No | β No | β No |
Attribute Order Preservation | β Yes | β No | β No | β No |
Entity Preservation | β Yes | β No | β No | β No |
Modern Java API | β Java 17+ | β Legacy | β Legacy | β Modern |
Stream Navigation | β Yes | β No | β No | β No |
Type Safety | β Compile-time | β Runtime | β Runtime | β Compile-time |
Performance | β Optimized | β Fast | β Fast | β Very Fast |
* JDOM Notes:
Format.getRawFormat()
preserves original whitespace between elementsFormat.getPrettyFormat()
reformats with consistent indentation- Text content whitespace configurable via
TextMode.PRESERVE/TRIM/NORMALIZE
Choose DomTrip when: You need perfect formatting preservation for config files, documentation, or human-edited XML Choose others when: You only need data extraction, transformation, or high-throughput processing
mvn test
Explore DomTrip features with interactive demos:
# Core editing features
mvn test-compile exec:java -Dexec.mainClass="eu.maveniverse.domtrip.demos.EditorDemo" -Dexec.classpathScope=test
# Builder patterns
mvn test-compile exec:java -Dexec.mainClass="eu.maveniverse.domtrip.demos.BuilderPatternsDemo" -Dexec.classpathScope=test
# Navigation features
mvn test-compile exec:java -Dexec.mainClass="eu.maveniverse.domtrip.demos.NavigationDemo" -Dexec.classpathScope=test
# Configuration options
mvn test-compile exec:java -Dexec.mainClass="eu.maveniverse.domtrip.demos.ConfigurationDemo" -Dexec.classpathScope=test
# Namespace handling
mvn test-compile exec:java -Dexec.mainClass="eu.maveniverse.domtrip.demos.NamespaceDemo" -Dexec.classpathScope=test
cd website
npm install
npm start # Development server
npm run build # Production build
Original XML:
<?xml version="1.0" encoding="UTF-8"?>
<!-- Sample Maven POM -->
<project xmlns="http://maven.apache.org/POM/4.0.0">
<groupId>com.example</groupId>
<artifactId>my-app</artifactId>
<version>1.0.0</version>
</project>
After editing with DomTrip:
<?xml version="1.0" encoding="UTF-8"?>
<!-- Sample Maven POM -->
<project xmlns="http://maven.apache.org/POM/4.0.0">
<groupId>com.example</groupId>
<artifactId>my-app</artifactId>
<version>2.0.0</version>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Notice how:
- β Original formatting preserved (XML declaration, comments, indentation)
- β
Only modified elements (
version
) and new elements (dependencies
) changed - β New elements follow inferred indentation patterns
- β Comments and namespace declarations maintained
We welcome contributions! Please see our Contributing Guide for details.
DomTrip is licensed under the Eclipse Public License 2.0.
- π Documentation
- π¦ Maven Central
- π Issues
- π¬ Discussions
DomTrip - Perfect XML round-trips, every time π