Element API
The Element class represents XML elements and provides comprehensive methods for manipulating element content, attributes, and whitespace while preserving formatting.
Overview
The Element class is the core building block for XML documents in DomTrip, providing:
- Element creation and manipulation
- Attribute management with formatting preservation
- Text content handling with whitespace preservation
- Child element navigation and modification
- Comprehensive whitespace control
- Namespace-aware operations
Element Creation
Factory Methods
// Create elements using factory methods
Element simple = Element.of("dependency");
Element withText = Element.of("groupId").textContent("com.example");
Element withNamespace = Element.of(QName.of("http://maven.apache.org/POM/4.0.0", "project"));
// Create empty element
Element empty = new Element("placeholder");
Assertions.assertNotNull(empty);
Assertions.assertNotNull(empty);
Builder Pattern
// Build complex elements with fluent API
Element dependency = Element.of("dependency")
.attribute("scope", "test")
.attribute("optional", "true")
.textContent("junit:junit:4.13.2");
// Chain multiple operations
Element project = Element.of("project").attribute("xmlns", "http://maven.apache.org/POM/4.0.0");
project.addChild(Element.of("groupId").textContent("com.example"));
project.addChild(Element.of("artifactId").textContent("my-app"));
project.addChild(Element.of("version").textContent("1.0.0"));
Attribute Management
Basic Attribute Operations
// Set attributes
element.attribute("groupId", "junit");
element.attribute("artifactId", "junit");
element.attribute("version", "4.13.2");
// Get attributes
String groupId = element.attribute("groupId"); // "junit"
String version = element.attribute("version"); // "4.13.2"
// Check if attribute exists
boolean hasScope = element.hasAttribute("scope"); // false
// Remove attribute
element.removeAttribute("version");
Attribute Formatting
Element element = Element.of("dependency");
// Set attribute with specific quote style
Attribute singleQuote = Attribute.of("groupId", "junit", QuoteStyle.SINGLE);
element.attributeObject("groupId", singleQuote);
// Set attribute with double quotes (default)
element.attribute("artifactId", "junit");
// Preserve existing quote style when updating
String xml = "<element attr='value'/>";
Document doc = Document.of(xml);
Element parsed = doc.root();
// Updating preserves the single quote style
parsed.attribute("attr", "new-value");
Text Content
Basic Text Operations
// Set text content
element.textContent("This is the description");
// Get text content
String content = element.textContent(); // "This is the description"
// Get trimmed content (removes leading/trailing whitespace)
String trimmed = element.textContentTrimmed();
Whitespace-Preserving Text
// Original content with whitespace: " old value "
String original = item.textContent();
// Update content while preserving whitespace pattern
item.textPreservingWhitespace("new value");
// Result maintains whitespace: " new value "
String updated = item.textContent();
Whitespace Management
DomTrip provides fine-grained control over whitespace at multiple levels:
Node-Level Whitespace
// Whitespace before the element
String preceding = element.precedingWhitespace(); // " "
// Set whitespace programmatically
element.precedingWhitespace(" ");
Element Tag Whitespace
// Whitespace inside opening tag: <element >
String openTag = element.openTagWhitespace(); // " "
// Whitespace inside closing tag: </ element>
String closeTag = element.closeTagWhitespace(); // " "
// Set tag whitespace
element.openTagWhitespace(" ");
element.closeTagWhitespace("");
Inner Element Whitespace
For elements containing only whitespace (no child elements), DomTrip provides specialized handling:
// Whitespace immediately before closing tag: WHITESPACE</parent>
String innerPreceding = parent.innerPrecedingWhitespace(); // "\n"
// Set inner whitespace for elements with only whitespace content
parent.innerPrecedingWhitespace("\n \n");
Child Element Navigation
Finding Child Elements
// Find first child with name
Optional<Element> groupId = project.childElement("groupId");
String group = groupId.map(Element::textContent).orElse("unknown");
// Find descendant anywhere in tree
Optional<Element> version = project.descendant("version");
// Check if child exists
boolean hasArtifactId = project.childElement("artifactId").isPresent();
Element Streams
// Stream all child elements
var allDeps = dependencies.childElements().toList();
// Stream children with specific name
var depElements = dependencies.childElements("dependency").toList();
// Filter and transform
var testDeps = dependencies
.childElements("dependency")
.filter(dep -> "test".equals(dep.attribute("scope")))
.map(Element::textContent)
.toList();
Namespace Support
Namespace-Aware Operations
// Create element with namespace
QName qname = QName.of("http://maven.apache.org/POM/4.0.0", "project");
Element project = Element.of(qname);
// Declare namespace
project.namespaceDeclaration("", "http://maven.apache.org/POM/4.0.0");
project.namespaceDeclaration("xsi", "http://www.w3.org/2001/XMLSchema-instance");
// Access namespace information
String namespaceURI = project.namespaceURI();
String localName = project.localName();
String prefix = project.prefix();
QName Support
// Create QName with namespace
QName projectQName = QName.of("http://maven.apache.org/POM/4.0.0", "project");
Element project = Element.of(projectQName);
Assertions.assertNotNull(project);
// Create QName with prefix
QName xsiQName = QName.of("http://www.w3.org/2001/XMLSchema-instance", "schemaLocation", "xsi");
// Access QName components
String namespaceURI = projectQName.namespaceURI();
String localName = projectQName.localName();
String prefix = xsiQName.prefix();
Element Modification
Adding Child Elements
Element parent = Element.of("dependencies");
// Add child element
Element dependency = Element.of("dependency");
parent.addChild(dependency);
// Add multiple children
dependency.addChild(Element.of("groupId").textContent("junit"));
dependency.addChild(Element.of("artifactId").textContent("junit"));
dependency.addChild(Element.of("version").textContent("4.13.2"));
// Add child with text
Element scope = Element.of("scope").textContent("test");
dependency.addChild(scope);
Child Positioning
Element parent = Element.of("dependencies");
Element first = Element.of("dependency").textContent("first");
Element second = Element.of("dependency").textContent("second");
parent.addChild(first);
parent.addChild(second);
// Insert before a specific child
Element beforeSecond = Element.of("dependency").textContent("before-second");
parent.insertChildBefore(second, beforeSecond);
// Insert after a specific child
Element afterFirst = Element.of("dependency").textContent("after-first");
parent.insertChildAfter(first, afterFirst);
// Replace a child node
Element replacement = Element.of("dependency").textContent("replaced");
parent.replaceChild(second, replacement);
// Access first and last children
Optional<Node> firstChild = parent.firstChild();
Optional<Node> lastChild = parent.lastChild();
// Get child count
int count = parent.childCount();
// Get child at index
Node childAtIndex = parent.child(0);
Removing Elements
// Find and remove specific element
Optional<Element> toRemove = project.childElement("dependency");
toRemove.ifPresent(project::removeChild);
// Remove all elements with specific name (collect to list first to avoid ConcurrentModificationException)
project.childElements("dependency").toList().forEach(project::removeChild);
// Remove by condition (collect to list first to avoid ConcurrentModificationException)
project.childElements()
.filter(child -> "deprecated".equals(child.attribute("status")))
.toList()
.forEach(project::removeChild);
Advanced Features
Element Cloning
Element original = Element.of("dependency").attribute("scope", "test").attribute("optional", "true");
original.addChild(Element.of("groupId").textContent("junit"));
original.addChild(Element.of("artifactId").textContent("junit"));
// Copy the element (deep copy)
Element clone = original.copy();
// Modify clone without affecting original
clone.attribute("scope", "compile");
clone.childElement("groupId").ifPresent(g -> g.textContent("mockito"));
Modification Tracking
// Check if element has been modified
boolean wasModified = element.isModified();
// Modify the element
element.attribute("attr", "new-value");
element.textContent("new content");
// Now it's marked as modified
boolean isModified = element.isModified();
// Clear modification flag (used internally by serializer)
element.clearModified();
Best Practices
✅ Do:
- Use
textPreservingWhitespace()when updating text content to maintain formatting - Leverage the fluent API for method chaining
- Use Optional-returning methods for safe navigation
- Set inner whitespace fields for elements with only whitespace content
- Use QName-based methods for namespace-aware operations
❌ Avoid:
- Directly manipulating the nodes list (use provided methods instead)
- Ignoring namespace context when working with namespaced elements
- Using
textContent()when you need to preserve whitespace patterns - Creating elements without proper namespace declarations
Integration with Editor
Elements work seamlessly with the Editor API for document-level operations:
Element root = editor.root();
// Use Editor methods for modifications
Element dependency = editor.addElement(root, "dependency");
editor.setAttribute(dependency, "scope", "test");
editor.addElement(dependency, "groupId", "junit");
// Use Element methods for navigation
Optional<Element> groupId = dependency.childElement("groupId");
String value = groupId.map(Element::textContent).orElse("unknown");
// Combine both approaches
dependency.childElements().forEach(child -> {
editor.setAttribute(child, "modified", "true");
});
The Element API provides the foundation for precise XML element manipulation while maintaining formatting integrity and supporting advanced features like namespaces and whitespace preservation.