Quick Start

Get up and running with DomTrip in 5 minutes! This guide covers the essential operations you'll use most often.

Your First DomTrip Program

Let's start with a simple example that demonstrates DomTrip's core strength: lossless round-trip editing.

// Parse XML while preserving all formatting
String originalXml =
        """
    <?xml version="1.0" encoding="UTF-8"?>
    <!-- Configuration file -->
    <config>
        <database>
            <host>localhost</host>
            <port>5432</port>
        </database>
    </config>
    """;

Document doc = Document.of(originalXml);
Editor editor = new Editor(doc);

// Make some changes
Element database = editor.root().descendant("database").orElseThrow();
editor.addElement(database, "username", "admin");
editor.addElement(database, "password", "secret");

// Serialize back to XML
String result = editor.toXml();
System.out.println(result);

Output:

<?xml version="1.0" encoding="UTF-8"?>
<!-- Configuration file -->
<config>
    <database>
        <host>localhost</host>
        <port>5432</port>
        <username>admin</username>
        <password>secret</password>
    </database>
</config>

Notice how:

  • The XML declaration and comments are preserved
  • Original indentation is maintained
  • New elements follow the existing formatting pattern

Core Operations

1. Loading XML

// From string
String xmlString = createConfigXml();
Document doc = Document.of(xmlString);
Editor editor = new Editor(doc);
// From file (recommended - handles encoding automatically)
// import java.nio.file.Path;
Document doc2 = Document.of(tempFile);
Editor editor2 = new Editor(doc2);
// From InputStream with automatic encoding detection
// import java.io.InputStream;
// import java.nio.file.Files;
// import java.nio.file.Path;
try (InputStream inputStream = Files.newInputStream(tempFile)) {
    Document doc3 = Document.of(inputStream);
    Editor editor3 = new Editor(doc3);
    Assertions.assertEquals("config", editor3.root().name());
}
// With custom configuration
Document docWithConfig = Document.of(xmlString);
Editor editorWithConfig = new Editor(docWithConfig, DomTripConfig.prettyPrint());

2. Finding Elements

String xml = createConfigXml();
Document doc = Document.of(xml);
Editor editor = new Editor(doc);

// Find by name
Element root = editor.root();
Element database = root.descendant("database").orElseThrow();

// Modern navigation with Optional
Optional<Element> host = database.child("host");
if (host.isPresent()) {
    System.out.println("Host: " + host.orElseThrow().textContent());
}
String xml = createConfigXml();
Document doc = Document.of(xml);
Editor editor = new Editor(doc);

// Stream-based navigation
editor.root()
        .descendants()
        .filter(e -> e.name().equals("port"))
        .findFirst()
        .ifPresent(port -> System.out.println("Port: " + port.textContent()));

3. Adding Elements

String xml = createTestXml("parent");
Document doc = Document.of(xml);
Editor editor = new Editor(doc);
Element parent = editor.root();

// Simple element with text
editor.addElement(parent, "name", "value");
// Element with attributes
Element element = editor.addElement(parent, "dependency");
editor.setAttribute(element, "scope", "test");
editor.addElement(element, "groupId", "junit");
String xml = createTestXml("parent");
Document doc = Document.of(xml);
Editor editor = new Editor(doc);
Element parent = editor.root();

// Using element builders for complex structures
Element dependency = editor.addElement(parent, "dependency");
editor.addElement(dependency, "groupId", "org.example");
editor.addElement(dependency, "artifactId", "my-library");
editor.addElement(dependency, "version", "1.0.0");
editor.setAttribute(dependency, "scope", "compile");

4. Modifying Content

String xml = createConfigXml();
Document doc = Document.of(xml);
Editor editor = new Editor(doc);

// Change text content
Element version = editor.root().descendant("version").orElseThrow();
editor.setTextContent(version, "2.0.0");

// Modify attributes
Element database = editor.root().descendant("database").orElseThrow();
editor.setAttribute(database, "id", "main-db");

// Add comments
editor.addComment(editor.root(), " This is a comment ");

5. Removing Elements

String xml = createConfigXml();
Document doc = Document.of(xml);
Editor editor = new Editor(doc);

// Remove element by reference
Element database = editor.root().descendant("database").orElseThrow();
editor.removeElement(database);

// Remove by finding first occurrence
Element version = editor.root().descendant("version").orElse(null);
if (version != null) {
    editor.removeElement(version);
}

Working with Namespaces

DomTrip provides excellent namespace support:

// Create elements with namespaces
Element soapEnvelope =
        Element.of("soap:Envelope").attribute("xmlns:soap", "http://schemas.xmlsoap.org/soap/envelope/");

// Namespace-aware navigation
String xml =
        """
    <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
        <soap:Body>
            <content>Hello</content>
        </soap:Body>
    </soap:Envelope>
    """;
Document doc = Document.of(xml);
Editor editor = new Editor(doc);
Element root = editor.root();

Optional<Element> body = root.child("soap:Body");
if (body.isPresent()) {
    System.out.println("Found SOAP body");
}

// Get namespace information
String localName = root.localName();
String namespaceURI = root.namespaceURI();

Configuration Options

Customize DomTrip's behavior with configuration:

String xml = createConfigXml();
Document doc = Document.of(xml);
Editor editor = new Editor(doc);

// Pretty printing
DomTripConfig config = DomTripConfig.prettyPrint();
String prettyXml = editor.toXml(config);

// Minimal output
String minimalXml = editor.toXml(DomTripConfig.minimal());

// Custom configuration
DomTripConfig custom = DomTripConfig.defaults()
        .withIndentString("  ") // 2 spaces
        .withWhitespacePreservation(true)
        .withCommentPreservation(true);
String customXml = editor.toXml(custom);

Real-World Example: Maven POM Editing

Here's a practical example of editing a Maven POM file:

public void addDependencyExample(String pomPath, String groupId, String artifactId, String version)
        throws Exception {
    // Load POM with automatic encoding detection
    Document doc = Document.of(Path.of(pomPath));
    Editor editor = new Editor(doc);

    // Find or create dependencies section
    Element project = editor.root();
    Element dependencies = project.descendant("dependencies").orElse(null);
    if (dependencies == null) {
        dependencies = editor.addElement(project, "dependencies");
    }

    // Add new dependency
    Element dependency = editor.addElement(dependencies, "dependency");
    editor.addElement(dependency, "groupId", groupId);
    editor.addElement(dependency, "artifactId", artifactId);
    editor.addElement(dependency, "version", version);

    // Save back to file (String-based)
    Files.writeString(Path.of(pomPath), editor.toXml());

    // Or save to OutputStream with proper encoding
    try (OutputStream outputStream = Files.newOutputStream(Path.of(pomPath))) {
        editor.document().toXml(outputStream);
    }

    System.out.println("✅ Added dependency: " + groupId + ":" + artifactId);
}

Working with Existing Documents

If you already have a parsed Document object, you can create an Editor from it:

// Parse with Document directly
String xmlString = createConfigXml();
Document document = Document.of(xmlString);

// Create Editor from existing Document
Editor editor = new Editor(document);

// Now use the convenient Editor API
Element root = editor.root();
editor.addElement(root, "newChild", "value");
editor.setAttribute(root, "version", "2.0");

// Serialize with preserved formatting
String result = editor.toXml();

You can also use custom configuration with existing documents:

// Create document programmatically
Document doc = Document.withRootElement("project");

// Create Editor with custom config
DomTripConfig config = DomTripConfig.prettyPrint().withIndentString("  ");
Editor editor = new Editor(doc, config);

// Build document structure
Element root = editor.root();
editor.addElement(root, "groupId", "com.example");
editor.addElement(root, "artifactId", "my-project");

Error Handling

DomTrip provides robust error handling:

try {
    // Attempt to parse malformed XML
    String malformedXml = "<root><unclosed>";
    Document doc = Document.of(malformedXml);

    // This won't be reached due to parsing error
    Editor editor = new Editor(doc);
} catch (Exception e) {
    // Handle parsing errors gracefully
    System.err.println("XML parsing failed: " + e.getMessage());

    // Provide fallback or user-friendly error message
    System.out.println("Please check your XML syntax and try again.");
}

// Safe navigation with Optional
String xml = createConfigXml();
Document doc = Document.of(xml);
Editor editor = new Editor(doc);

editor.root()
        .descendant("nonexistent")
        .ifPresentOrElse(
                element -> System.out.println("Found: " + element.name()),
                () -> System.out.println("Element not found - using default behavior"));

Next Steps

Now that you've mastered the basics, explore more advanced features:

Tips for Success

  1. Always use try-with-resources for file operations
  2. Check for null when finding elements that might not exist
  3. Use Optional for safer navigation
  4. Leverage Stream API for filtering and processing collections
  5. Configure appropriately for your use case (preserve vs. pretty print)