Flexible XML framework for Java providing comprehensive XML processing capabilities
—
This section covers DOM4J's advanced capabilities including JAXB integration, JavaBean binding, XML Schema datatype support, DTD processing, rule-based processing, Swing integration, and other specialized features for enterprise XML processing scenarios.
DOM4J provides comprehensive integration with JAXB (Java Architecture for XML Binding) for object-to-XML mapping and vice versa.
import org.dom4j.jaxb.JAXBReader;
import org.dom4j.jaxb.JAXBWriter;
import org.dom4j.jaxb.JAXBSupport;
import org.dom4j.jaxb.JAXBObjectHandler;
import org.dom4j.jaxb.JAXBModifier;
import org.dom4j.jaxb.JAXBRuntimeException;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.Marshaller;public class JAXBReader extends SAXReader {
// Constructors
public JAXBReader(String contextPath);
public JAXBReader(String contextPath, ClassLoader classLoader);
public JAXBReader(JAXBContext jaxbContext);
// Object handling
public void setObjectHandler(JAXBObjectHandler objectHandler);
public JAXBObjectHandler getObjectHandler();
// JAXB context
public JAXBContext getJAXBContext();
public Unmarshaller getUnmarshaller() throws JAXBException;
}// Define JAXB classes
@XmlRootElement(name = "book")
@XmlAccessorType(XmlAccessType.FIELD)
public class Book {
@XmlAttribute
private String isbn;
@XmlElement
private String title;
@XmlElement
private String author;
@XmlElement
private BigDecimal price;
// Constructors, getters, setters...
}
@XmlRootElement(name = "catalog")
@XmlAccessorType(XmlAccessType.FIELD)
public class Catalog {
@XmlElement(name = "book")
private List<Book> books = new ArrayList<>();
// Constructors, getters, setters...
}
// Create JAXBReader
JAXBContext jaxbContext = JAXBContext.newInstance(Catalog.class, Book.class);
JAXBReader reader = new JAXBReader(jaxbContext);
// Object handler to process unmarshalled objects
reader.setObjectHandler(new JAXBObjectHandler() {
@Override
public void handleObject(Object object) throws Exception {
if (object instanceof Book) {
Book book = (Book) object;
System.out.println("Processed book: " + book.getTitle());
// Process book object
} else if (object instanceof Catalog) {
Catalog catalog = (Catalog) object;
System.out.println("Processed catalog with " + catalog.getBooks().size() + " books");
}
}
});
// Read and process XML
Document document = reader.read("catalog.xml");
// Objects are automatically unmarshalled and processed through handler
// Access unmarshalled objects from document
Catalog catalog = (Catalog) document.getRootElement().getData();public class JAXBWriter extends XMLWriter {
// Constructors
public JAXBWriter(String contextPath);
public JAXBWriter(String contextPath, ClassLoader classLoader);
public JAXBWriter(JAXBContext jaxbContext);
public JAXBWriter(String contextPath, Writer writer);
public JAXBWriter(JAXBContext jaxbContext, Writer writer, OutputFormat format);
// Object writing
public void writeObject(Object object) throws IOException, JAXBException;
public void writeCloseObject(Object object) throws IOException;
public void writeOpenObject(Object object) throws IOException;
// JAXB context
public JAXBContext getJAXBContext();
public Marshaller getMarshaller() throws JAXBException;
}// Create objects to marshal
Catalog catalog = new Catalog();
catalog.getBooks().add(new Book("978-1234567890", "XML Processing", "John Doe", new BigDecimal("29.99")));
catalog.getBooks().add(new Book("978-0987654321", "JAXB Guide", "Jane Smith", new BigDecimal("34.99")));
// Create JAXBWriter
JAXBContext jaxbContext = JAXBContext.newInstance(Catalog.class, Book.class);
try (FileWriter writer = new FileWriter("output.xml")) {
OutputFormat format = OutputFormat.createPrettyPrint();
JAXBWriter jaxbWriter = new JAXBWriter(jaxbContext, writer, format);
// Write object as XML
jaxbWriter.writeObject(catalog);
}
// Stream writing for large collections
try (FileWriter writer = new FileWriter("streaming.xml")) {
JAXBWriter jaxbWriter = new JAXBWriter(jaxbContext, writer);
// Manual document structure with object marshalling
jaxbWriter.writeOpen(new Catalog());
for (Book book : getAllBooks()) {
jaxbWriter.writeObject(book);
}
jaxbWriter.writeClose(new Catalog());
}public class JAXBModifier extends SAXModifier {
public JAXBModifier(String contextPath);
public JAXBModifier(JAXBContext jaxbContext);
public void addObjectHandler(String path, JAXBObjectHandler handler);
public void removeObjectHandler(String path);
}// Modify documents using JAXB objects
JAXBContext jaxbContext = JAXBContext.newInstance(Book.class);
JAXBModifier modifier = new JAXBModifier(jaxbContext);
// Add handler for specific elements
modifier.addObjectHandler("/catalog/book", new JAXBObjectHandler() {
@Override
public void handleObject(Object object) throws Exception {
if (object instanceof Book) {
Book book = (Book) object;
// Modify book object
if (book.getPrice().compareTo(new BigDecimal("50")) > 0) {
book.setPrice(book.getPrice().multiply(new BigDecimal("0.9"))); // 10% discount
}
// Return modified object
return book;
}
}
});
// Process document
Document modifiedDoc = modifier.modify(originalDocument);DOM4J provides reflection-based XML binding through the bean package for automatic JavaBean to XML mapping.
import org.dom4j.bean.BeanElement;
import org.dom4j.bean.BeanAttribute;
import org.dom4j.bean.BeanDocumentFactory;
import org.dom4j.bean.BeanMetaData;public class BeanElement extends DefaultElement {
// Constructors
public BeanElement(String name, Object bean);
public BeanElement(QName qname, Object bean);
// Bean access
public Object getBean();
public void setBean(Object bean);
// Attribute mapping
public BeanMetaData getBeanMetaData();
}// JavaBean class
public class Product {
private String id;
private String name;
private String category;
private double price;
private boolean available;
// Standard getters and setters
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getCategory() { return category; }
public void setCategory(String category) { this.category = category; }
public double getPrice() { return price; }
public void setPrice(double price) { this.price = price; }
public boolean isAvailable() { return available; }
public void setAvailable(boolean available) { this.available = available; }
}
// Create BeanElement from JavaBean
Product product = new Product();
product.setId("P123");
product.setName("XML Parser");
product.setCategory("Software");
product.setPrice(99.99);
product.setAvailable(true);
// Create document with bean elements
Document document = DocumentHelper.createDocument();
BeanElement productElement = new BeanElement("product", product);
document.setRootElement(productElement);
// Bean properties become XML attributes and elements automatically
String xml = document.asXML();
// Results in XML reflecting bean properties
// Read bean data from XML
String value = productElement.attributeValue("id"); // "P123"
boolean available = Boolean.parseBoolean(productElement.attributeValue("available")); // true
// Modify bean through XML
productElement.addAttribute("price", "89.99");
// This updates the underlying bean object
double newPrice = product.getPrice(); // 89.99public class BeanDocumentFactory extends DocumentFactory {
public BeanDocumentFactory();
@Override
public Element createElement(QName qname);
@Override
public Attribute createAttribute(Element owner, QName qname, String value);
}// Use bean-aware factory
BeanDocumentFactory beanFactory = new BeanDocumentFactory();
SAXReader reader = new SAXReader();
reader.setDocumentFactory(beanFactory);
// Documents created with bean support
Document beanDocument = reader.read("products.xml");
// Elements automatically become BeanElements where appropriate
Element root = beanDocument.getRootElement();
if (root instanceof BeanElement) {
BeanElement beanRoot = (BeanElement) root;
Object bean = beanRoot.getBean();
// Work with underlying bean object
}DOM4J provides XML Schema datatype integration for type-safe XML processing through the datatype package.
import org.dom4j.datatype.DatatypeDocumentFactory;
import org.dom4j.datatype.DatatypeElement;
import org.dom4j.datatype.DatatypeAttribute;
import org.dom4j.datatype.DatatypeElementFactory;
import org.dom4j.datatype.InvalidSchemaException;public class DatatypeDocumentFactory extends DocumentFactory {
// Constructors
public DatatypeDocumentFactory();
// Schema loading
public void loadSchema(Document schemaDocument) throws InvalidSchemaException;
public void loadSchema(Document schemaDocument, String uri) throws InvalidSchemaException;
// Datatype-aware creation
@Override
public Element createElement(QName qname);
@Override
public Attribute createAttribute(Element owner, QName qname, String value);
}// Load XML Schema
Document schemaDoc = new SAXReader().read("product-schema.xsd");
// Create datatype-aware factory
DatatypeDocumentFactory datatypeFactory = new DatatypeDocumentFactory();
datatypeFactory.loadSchema(schemaDoc);
// Use with SAX reader
SAXReader reader = new SAXReader();
reader.setDocumentFactory(datatypeFactory);
// Parse document with datatype support
Document document = reader.read("products.xml");
// Elements have datatype information
Element priceElement = (Element) document.selectSingleNode("//price");
if (priceElement instanceof DatatypeElement) {
DatatypeElement dtPrice = (DatatypeElement) priceElement;
// Get typed data
Object typedValue = dtPrice.getData(); // Returns appropriate Java type
if (typedValue instanceof BigDecimal) {
BigDecimal price = (BigDecimal) typedValue;
System.out.println("Price: " + price);
}
// Schema validation
boolean valid = dtPrice.isValid();
if (!valid) {
String error = dtPrice.getValidationMessage();
System.err.println("Validation error: " + error);
}
}
// Attributes also have datatype support
Attribute idAttr = priceElement.attribute("id");
if (idAttr instanceof DatatypeAttribute) {
DatatypeAttribute dtId = (DatatypeAttribute) idAttr;
Object idValue = dtId.getData(); // Typed according to schema
}// Implement custom datatype element factory
public class CustomDatatypeElementFactory implements DatatypeElementFactory {
@Override
public Element createElement(QName qname, XSType xsType) {
if ("decimal".equals(xsType.getName())) {
return new CurrencyElement(qname, xsType);
}
return new DatatypeElement(qname, xsType);
}
}
// Custom currency element with business logic
class CurrencyElement extends DatatypeElement {
public CurrencyElement(QName qname, XSType xsType) {
super(qname, xsType);
}
@Override
public void setText(String text) {
// Custom validation and formatting for currency
try {
BigDecimal amount = new BigDecimal(text);
if (amount.scale() > 2) {
amount = amount.setScale(2, RoundingMode.HALF_UP);
}
super.setText(amount.toString());
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Invalid currency format: " + text);
}
}
public BigDecimal getCurrencyValue() {
return (BigDecimal) getData();
}
}
// Use custom factory
DatatypeDocumentFactory factory = new DatatypeDocumentFactory();
factory.setDatatypeElementFactory(new CustomDatatypeElementFactory());DOM4J provides XSLT-style pattern matching and rule-based processing through the rule package.
import org.dom4j.rule.Pattern;
import org.dom4j.rule.Rule;
import org.dom4j.rule.RuleSet;
import org.dom4j.rule.Stylesheet;
import org.dom4j.rule.Action;public interface Pattern extends NodeFilter {
// Pattern matching priority (higher = more specific)
double getPriority();
// Match mode support
String getMatchesNodeName();
// Union pattern support
Pattern[] getUnionPatterns();
// Pattern properties
short getMatchType();
String getMatchesNodeName();
}public class Rule {
// Constructors
public Rule();
public Rule(Pattern pattern);
public Rule(Pattern pattern, Action action);
// Pattern and action
public Pattern getPattern();
public void setPattern(Pattern pattern);
public Action getAction();
public void setAction(Action action);
// Execution
public void fireRule(Node node) throws Exception;
}
public class RuleSet {
// Rule management
public void addRule(Rule rule);
public void removeRule(Rule rule);
public Rule getMatchingRule(Node node);
// Rule execution
public void fireRule(Node node) throws Exception;
}// Define processing actions
Action bookAction = new Action() {
@Override
public void run(Node node) throws Exception {
Element book = (Element) node;
String title = book.elementText("title");
String author = book.elementText("author");
System.out.println("Processing book: " + title + " by " + author);
// Add processing timestamp
book.addAttribute("processed", Instant.now().toString());
}
};
Action priceAction = new Action() {
@Override
public void run(Node node) throws Exception {
Element priceElement = (Element) node;
String priceText = priceElement.getText();
try {
BigDecimal price = new BigDecimal(priceText);
// Format price to 2 decimal places
String formatted = price.setScale(2, RoundingMode.HALF_UP).toString();
priceElement.setText(formatted);
} catch (NumberFormatException e) {
System.err.println("Invalid price: " + priceText);
}
}
};
// Create patterns and rules
Pattern bookPattern = DocumentHelper.createPattern("book");
Pattern pricePattern = DocumentHelper.createPattern("book/price");
Rule bookRule = new Rule(bookPattern, bookAction);
Rule priceRule = new Rule(pricePattern, priceAction);
// Create rule set
RuleSet ruleSet = new RuleSet();
ruleSet.addRule(bookRule);
ruleSet.addRule(priceRule);
// Apply rules to document
Document document = loadDocument();
applyRules(document, ruleSet);
// Recursive rule application
private void applyRules(Node node, RuleSet ruleSet) throws Exception {
// Apply rule to current node
Rule rule = ruleSet.getMatchingRule(node);
if (rule != null) {
rule.fireRule(node);
}
// Apply rules to children
if (node instanceof Branch) {
Branch branch = (Branch) node;
for (int i = 0; i < branch.nodeCount(); i++) {
applyRules(branch.node(i), ruleSet);
}
}
}public class Stylesheet {
// Template management
public void addTemplate(Rule rule);
public void setModeName(String modeName);
public String getModeName();
// Processing modes
public void applyTemplates(List<Node> list) throws Exception;
public void applyTemplates(List<Node> list, String mode) throws Exception;
public void run(List<Node> list) throws Exception;
public void run(List<Node> list, String mode) throws Exception;
}// Create stylesheet with templates
Stylesheet stylesheet = new Stylesheet();
// Template for catalog root
stylesheet.addTemplate(new Rule(
DocumentHelper.createPattern("catalog"),
new Action() {
@Override
public void run(Node node) throws Exception {
System.out.println("Processing catalog...");
Element catalog = (Element) node;
// Process children with different mode
List<Node> books = catalog.selectNodes("book");
stylesheet.applyTemplates(books, "summary");
}
}
));
// Template for books in summary mode
Rule summaryRule = new Rule(
DocumentHelper.createPattern("book"),
new Action() {
@Override
public void run(Node node) throws Exception {
Element book = (Element) node;
String title = book.elementText("title");
String isbn = book.attributeValue("isbn");
System.out.println("Book summary: " + title + " (ISBN: " + isbn + ")");
}
}
);
summaryRule.setMode("summary");
stylesheet.addTemplate(summaryRule);
// Apply stylesheet
Document document = loadCatalog();
List<Node> roots = Arrays.asList(document.getRootElement());
stylesheet.run(roots);DOM4J provides support for DTD (Document Type Definition) processing and validation.
import org.dom4j.DocumentType;
import org.dom4j.dtd.*;public interface DocumentType extends Node {
// DTD properties
String getElementName();
void setElementName(String elementName);
String getPublicID();
void setPublicID(String publicID);
String getSystemID();
void setSystemID(String systemID);
// Internal subset
List getInternalDeclarations();
void setInternalDeclarations(List internalDeclarations);
// External subset
List getExternalDeclarations();
void setExternalDeclarations(List externalDeclarations);
}// Create document with DTD
Document document = DocumentHelper.createDocument();
// Add DOCTYPE declaration
DocumentType docType = DocumentHelper.createDocType(
"catalog", // Root element name
"-//Example//DTD Catalog 1.0//EN", // Public ID
"http://example.com/dtd/catalog.dtd" // System ID
);
document.setDocType(docType);
// Parse document with DTD validation
SAXReader reader = new SAXReader(true); // Enable validation
// Custom entity resolver for DTD
reader.setEntityResolver(new EntityResolver() {
@Override
public InputSource resolveEntity(String publicId, String systemId) throws IOException {
if (systemId.endsWith("catalog.dtd")) {
// Return local DTD copy
return new InputSource(new FileInputStream("local-catalog.dtd"));
}
return null; // Use default resolution
}
});
try {
Document validatedDoc = reader.read("catalog.xml");
System.out.println("Document is valid according to DTD");
} catch (DocumentException e) {
System.err.println("DTD validation failed: " + e.getMessage());
}
// Access DTD information
DocumentType docTypeInfo = document.getDocType();
if (docTypeInfo != null) {
String rootElement = docTypeInfo.getElementName();
String publicId = docTypeInfo.getPublicID();
String systemId = docTypeInfo.getSystemID();
System.out.printf("DTD: %s PUBLIC '%s' '%s'%n", rootElement, publicId, systemId);
}DOM4J provides Swing integration for creating tree and table models from XML documents.
import org.dom4j.swing.DocumentTreeModel;
import org.dom4j.swing.XMLTableModel;
import org.dom4j.swing.XMLTableDefinition;
import javax.swing.*;
import javax.swing.tree.TreeModel;
import javax.swing.table.TableModel;public class DocumentTreeModel implements TreeModel {
// Constructors
public DocumentTreeModel(Document document);
public DocumentTreeModel(Node rootNode);
// TreeModel implementation
@Override
public Object getRoot();
@Override
public Object getChild(Object parent, int index);
@Override
public int getChildCount(Object parent);
@Override
public boolean isLeaf(Object node);
@Override
public int getIndexOfChild(Object parent, Object child);
// Tree event support
@Override
public void addTreeModelListener(TreeModelListener listener);
@Override
public void removeTreeModelListener(TreeModelListener listener);
}// Create JTree from XML document
Document document = loadXMLDocument();
DocumentTreeModel treeModel = new DocumentTreeModel(document);
JTree xmlTree = new JTree(treeModel);
// Customize tree rendering
xmlTree.setCellRenderer(new DefaultTreeCellRenderer() {
@Override
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected,
boolean expanded, boolean leaf, int row, boolean hasFocus) {
Component component = super.getTreeCellRendererComponent(
tree, value, selected, expanded, leaf, row, hasFocus);
if (value instanceof Element) {
Element element = (Element) value;
setText(element.getName());
setIcon(getElementIcon(element));
} else if (value instanceof Attribute) {
Attribute attr = (Attribute) value;
setText("@" + attr.getName() + " = " + attr.getValue());
setIcon(getAttributeIcon());
} else if (value instanceof Text) {
Text text = (Text) value;
setText("\"" + text.getText().trim() + "\"");
setIcon(getTextIcon());
}
return component;
}
});
// Add to Swing component
JScrollPane treeScrollPane = new JScrollPane(xmlTree);
frame.add(treeScrollPane, BorderLayout.CENTER);public class XMLTableModel implements TableModel {
// Constructors
public XMLTableModel(XMLTableDefinition definition, Document document);
public XMLTableModel(XMLTableDefinition definition, List<Node> rows);
// TableModel implementation
@Override
public int getRowCount();
@Override
public int getColumnCount();
@Override
public String getColumnName(int columnIndex);
@Override
public Class<?> getColumnClass(int columnIndex);
@Override
public boolean isCellEditable(int rowIndex, int columnIndex);
@Override
public Object getValueAt(int rowIndex, int columnIndex);
@Override
public void setValueAt(Object value, int rowIndex, int columnIndex);
// Table event support
@Override
public void addTableModelListener(TableModelListener listener);
@Override
public void removeTableModelListener(TableModelListener listener);
}public class XMLTableDefinition {
// Constructors
public XMLTableDefinition();
// Row definition
public void setRowExpression(String rowXPath);
public String getRowExpression();
// Column definition
public void addColumnDefinition(String columnName, String columnXPath);
public void addColumnDefinition(String columnName, String columnXPath, Class<?> columnClass);
// Column access
public int getColumnCount();
public String getColumnName(int index);
public String getColumnXPath(int index);
public Class<?> getColumnClass(int index);
}// Define table structure
XMLTableDefinition tableDef = new XMLTableDefinition();
tableDef.setRowExpression("//book"); // Each book is a row
// Define columns
tableDef.addColumnDefinition("Title", "title/text()", String.class);
tableDef.addColumnDefinition("Author", "author/text()", String.class);
tableDef.addColumnDefinition("ISBN", "@isbn", String.class);
tableDef.addColumnDefinition("Price", "@price", Double.class);
tableDef.addColumnDefinition("Available", "@available", Boolean.class);
// Create table model
Document catalog = loadCatalog();
XMLTableModel tableModel = new XMLTableModel(tableDef, catalog);
// Create JTable
JTable xmlTable = new JTable(tableModel);
// Configure table
xmlTable.setAutoCreateRowSorter(true);
xmlTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
// Custom cell renderers
xmlTable.getColumnModel().getColumn(3).setCellRenderer(new DefaultTableCellRenderer() {
private final NumberFormat currencyFormat = NumberFormat.getCurrencyInstance();
@Override
public void setValue(Object value) {
if (value instanceof Number) {
setText(currencyFormat.format(((Number) value).doubleValue()));
} else {
setText(value != null ? value.toString() : "");
}
}
});
// Add to Swing interface
JScrollPane tableScrollPane = new JScrollPane(xmlTable);
frame.add(tableScrollPane, BorderLayout.CENTER);
// Handle selection changes
xmlTable.getSelectionModel().addListSelectionListener(e -> {
if (!e.getValueIsAdjusting()) {
int selectedRow = xmlTable.getSelectedRow();
if (selectedRow >= 0) {
String title = (String) tableModel.getValueAt(selectedRow, 0);
System.out.println("Selected book: " + title);
}
}
});import org.dom4j.util.SingletonStrategy;
import org.dom4j.util.SimpleSingleton;
// Custom singleton strategy
public class ThreadLocalSingletonStrategy<T> implements SingletonStrategy<T> {
private final ThreadLocal<T> threadLocal = new ThreadLocal<>();
private final Class<T> type;
public ThreadLocalSingletonStrategy(Class<T> type) {
this.type = type;
}
@Override
public T instance() {
T instance = threadLocal.get();
if (instance == null) {
try {
instance = type.newInstance();
threadLocal.set(instance);
} catch (Exception e) {
throw new RuntimeException("Cannot create instance", e);
}
}
return instance;
}
}
// Use with document factory
SingletonStrategy<DocumentFactory> strategy = new ThreadLocalSingletonStrategy<>(DocumentFactory.class);
DocumentFactory factory = strategy.instance();import org.dom4j.util.UserDataElement;
import org.dom4j.util.UserDataAttribute;
import org.dom4j.util.UserDataDocumentFactory;
// Elements that can store arbitrary user data
UserDataDocumentFactory userDataFactory = new UserDataDocumentFactory();
Document document = userDataFactory.createDocument();
Element element = userDataFactory.createElement("product");
if (element instanceof UserDataElement) {
UserDataElement userElement = (UserDataElement) element;
// Store custom application data
userElement.setData("businessObject", productBusinessObject);
userElement.setData("validationState", ValidationState.VALID);
userElement.setData("lastModified", Instant.now());
// Retrieve user data
Product businessObj = (Product) userElement.getData("businessObject");
ValidationState state = (ValidationState) userElement.getData("validationState");
}import org.dom4j.util.XMLErrorHandler;
// Comprehensive error handling
XMLErrorHandler errorHandler = new XMLErrorHandler();
// Configure error levels
errorHandler.setIgnoreWarnings(false);
errorHandler.setIgnoreErrors(false);
SAXReader reader = new SAXReader();
reader.setErrorHandler(errorHandler);
try {
Document document = reader.read("document.xml");
// Check for warnings and errors after parsing
if (errorHandler.hasErrors()) {
List<String> errors = errorHandler.getErrors();
System.err.println("Parsing errors occurred:");
errors.forEach(System.err::println);
}
if (errorHandler.hasWarnings()) {
List<String> warnings = errorHandler.getWarnings();
System.out.println("Parsing warnings:");
warnings.forEach(System.out::println);
}
} catch (DocumentException e) {
System.err.println("Fatal parsing error: " + e.getMessage());
}import org.dom4j.util.NodeComparator;
import java.util.Comparator;
public class NodeComparator implements Comparator<Node> {
// Constructors
public NodeComparator();
// Comparison methods
public int compare(Node node1, Node node2);
// Comparison by different criteria
public static Comparator<Node> createDocumentOrderComparator();
public static Comparator<Node> createNameComparator();
public static Comparator<Node> createTextComparator();
}Usage Examples:
import org.dom4j.util.NodeComparator;
// Sort elements by name
List<Element> elements = document.selectNodes("//element");
elements.sort(NodeComparator.createNameComparator());
// Sort by document order
List<Node> nodes = document.selectNodes("//node()");
nodes.sort(new NodeComparator());
// Custom sorting
elements.sort((e1, e2) -> {
NodeComparator comparator = new NodeComparator();
int nameCompare = e1.getName().compareTo(e2.getName());
return nameCompare != 0 ? nameCompare : comparator.compare(e1, e2);
});import org.dom4j.util.IndexedElement;
import org.dom4j.util.IndexedDocumentFactory;
public class IndexedElement extends DefaultElement {
// Constructor
public IndexedElement(QName qname);
public IndexedElement(String name);
// Indexed access methods
public Element elementByID(String elementID);
public List<Element> elementsByName(QName qname);
public List<Element> elementsByName(String name);
// Index management
public void indexElements();
public boolean isIndexed();
}
public class IndexedDocumentFactory extends DocumentFactory {
// Singleton access
public static IndexedDocumentFactory getInstance();
// Override factory methods to create indexed elements
public Element createElement(QName qname);
public Element createElement(String name);
}Usage Examples:
// Create document with indexed elements
IndexedDocumentFactory factory = IndexedDocumentFactory.getInstance();
SAXReader reader = new SAXReader();
reader.setDocumentFactory(factory);
Document document = reader.read("large-document.xml");
Element root = document.getRootElement();
if (root instanceof IndexedElement) {
IndexedElement indexedRoot = (IndexedElement) root;
// Fast element lookup by ID
Element elementById = indexedRoot.elementByID("product-123");
// Fast lookup by name (faster than XPath for large documents)
List<Element> products = indexedRoot.elementsByName("product");
List<Element> categories = indexedRoot.elementsByName(QName.get("category", namespace));
}// Custom lightweight implementations
public class LightweightDocumentFactory extends DocumentFactory {
@Override
public Element createElement(QName qname) {
return new LightweightElement(qname);
}
@Override
public Document createDocument() {
return new LightweightDocument();
}
}
// Optimized for memory usage
class LightweightElement extends AbstractElement {
private QName qname;
private Object content; // Single object or List for multiple children
private Object attributes; // Single attribute or Map for multiple
public LightweightElement(QName qname) {
this.qname = qname;
}
// Optimized implementations that use less memory
// ... implementation details
}// XPath compilation caching
public class CachedXPathProcessor {
private final Map<String, XPath> xpathCache = new ConcurrentHashMap<>();
private final Map<String, String> namespaces;
public CachedXPathProcessor(Map<String, String> namespaces) {
this.namespaces = namespaces != null ? Map.copyOf(namespaces) : Map.of();
}
public XPath getOrCreateXPath(String expression) {
return xpathCache.computeIfAbsent(expression, expr -> {
try {
XPath xpath = DocumentHelper.createXPath(expr);
if (!namespaces.isEmpty()) {
xpath.setNamespaceURIs(namespaces);
}
return xpath;
} catch (InvalidXPathException e) {
throw new RuntimeException("Invalid XPath: " + expr, e);
}
});
}
// Batch processing methods
public Map<String, List<Node>> executeQueries(Document document, List<String> expressions) {
return expressions.parallelStream()
.collect(Collectors.toConcurrentMap(
expr -> expr,
expr -> getOrCreateXPath(expr).selectNodes(document)
));
}
}DOM4J's advanced features provide enterprise-grade capabilities for complex XML processing scenarios. From object binding and schema validation to rule-based processing and GUI integration, these features enable sophisticated XML applications while maintaining DOM4J's core principles of flexibility and ease of use.
Install with Tessl CLI
npx tessl i tessl/maven-org-dom4j--dom4j