Skip to content

Latest commit

 

History

History
326 lines (248 loc) · 12.2 KB

File metadata and controls

326 lines (248 loc) · 12.2 KB

DomTrip

Lossless XML Editing for Java

Maven Central GitHub Documentation

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.

🎯 Why DomTrip?

  • 🔄 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

🚀 Quick Start

Installation

Add DomTrip to your Maven project:

<dependency>
    <groupId>eu.maveniverse.maven.domtrip</groupId>
    <artifactId>domtrip-maven</artifactId>
    <version>0.2.0</version>
</dependency>

Basic Usage

// 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();

✨ Key Features

Lossless Preservation

  • Comments: Multi-line comments, inline comments, document comments
  • Whitespace: Exact spacing, indentation, and line breaks
  • Entities: Custom entity encoding (&lt;, &amp;, &quot;)
  • Attributes: Quote styles (single vs double quotes), ordering, spacing
  • Declarations: XML declarations, processing instructions, DTDs

Modern Java API

  • 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 Support

  • 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

📚 Documentation

📖 Complete Documentation

🧪 Quality Assurance

  • 📊 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

🏗️ Advanced Features

Factory Methods

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");

Fluent API

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"));

Stream-Based Navigation

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("")));

Namespace-Aware Operations

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");

Configuration Management

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);

🏛️ Architecture

DomTrip uses a clean, type-safe architecture that enforces XML structure rules:

Node Hierarchy

Node (abstract base)
├── ContainerNode (abstract)
│   ├── Document (root container)
│   └── Element (XML elements)
└── Leaf Nodes
    ├── Text (text content, CDATA)
    ├── Comment (XML comments)
    └── ProcessingInstruction (PIs)

Core Components

  • Editor - High-level editing interface with formatting preservation
  • Parser - XML parsing engine that captures all formatting metadata
  • Serializer - Minimal-change XML output generation
  • DomTripConfig - Configuration management with presets

Design Principles

  1. 🔒 Preservation First - Original formatting preserved unless explicitly modified
  2. 📊 Metadata Tracking - Each node tracks modification state and formatting
  3. ⚡ Minimal Changes - Only modified sections are reformatted
  4. 🛡️ Type Safety - Compile-time prevention of invalid operations

💡 Use Cases

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

🎯 Comparison with Other Libraries

Feature DomTrip DOM4J JDOM Jackson XML
Lossless Round-Trip ✅ Perfect ❌ No ❌ No ❌ No
Comment Preservation ✅ Yes ✅ Yes ✅ Yes ❌ No
Between-Element Whitespace ✅ Exact ⚠️ Partial ✅ Yes* ❌ No
In-Element Whitespace ✅ Exact ❌ No ⚠️ Configurable ❌ 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 elements
  • Format.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

🧪 Development

Running Tests

mvn test

Interactive Demos

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

Building Documentation

cd website
npm install
npm start  # Development server
npm run build  # Production build

📋 Example: Before and After

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

🤝 Contributing

We welcome contributions! Please see our Contributing Guide for details.

📄 License

DomTrip is licensed under the Eclipse Public License 2.0.

🔗 Links


DomTrip - Perfect XML round-trips, every time 🔄