- Predicates on
VElement#child
andVElement#children
methods VElement#child
andVElement#children
methods to retrieve direct children.VElement#xPath
to retrieve elements based on xPath expressions.
Writing or editing Dom/XML in Java has never been natural or straightforward. The Java code structure diverges inexorably from the underlying tree structure it manipulates, obscuring the original intentions.
Vincer-Dom fixes this issue by using Parent-Chaining Pattern.
Vincer-Dom simply wraps org.w3c.dom.Document
and org.w3c.dom.Element
from JDK to offer a powerful fresh new style API.
To give a concrete idea, the following code:
- Reads a pom.xml file (an XML file from Maven that most of Java developers know)
- Adds 2 dependencies
- Removes all dependencies having
test
scope - Modifies distribution repository, creating potential missing elements.
public class EditTest {
@Test
public void editMavenPom() {
InputStream stream = EditTest.class.getResourceAsStream("sample-pom.xml");
VDocument doc = VDocument.parse(stream)
.root()
.get("dependencies")
.add("dependency")
.add("groupId").text("com.github.djeang").__
.add("artifactId").text("vincer-dom").__
.add("version").text("0.1-SNAPSHOT").__.__
.add("dependency")
.add("groupId").text("org.junit.jupiter").__
.add("artifactId").text("junit-jupiter-engine").__
.add("version").text("5.4.0").__
.add("scope").text("test").__.__
.apply(this::removeTests).__
.get("distributionManagement") // The distributionManagement tag may be present or not
.get("repository")
.get("id").make().text("My repo id").__ // make() creates absent element and its ancestors
.get("name").make().text("My repo name").__
.get("url").make().text("http://myserver::8081").__.__.__.__;
doc.print(System.out);
}
private void removeTests(VElement<?> dependencies) {
dependencies.getAll("dependency").stream()
.filter(dependency -> "test".equals(dependency.get("scope").getText()))
.forEach(VElement::remove);
}
}
The complete Dom manipulation has been expressed in a single chained statement, reflecting the tree structure of the manipulated data.
Vincer-dom makes also parsing easier. By proxying non-existing element, it removes the need of null checking parent.
For example, this prints all dependencies and plugins found in a pom. If <version>
tag does not exist, the text()
method will return null
instead of throwing a NullPointerException
.
void printAllDeps() {
VElement root = VDocument.parse(EditTest.class.getResourceAsStream("sample-pom.xml")).root();
root
.child("dependencies")
.children("dependency").forEach(this::printDependency);
System.out.println("---");
root.xPath("build/plugins/plugin").forEach(this::printDependency);
}
private void printDependency(VElement el) {
System.out.println(String.format("%s:%s:%s",
el.get("groupId").text(),
el.get("artifactId").text(),
el.get("version").text()));
}
Output :
mysql:mysql-connector-java:5.1.14
org.slf4j:slf4j-simple:1.6.1
---
org.apache.maven.plugins:maven-compiler-plugin:2.3.2
org.apache.maven.plugins:maven-jar-plugin:null
org.apache.maven.plugins:maven-shade-plugin:1.4
As you can see, Vincer-Dom saves a lot of coding effort while getting code much more readable.
With the use of Parent-Chaining pattern, the API is very thin as it consists of only
2 classes : VDocument
and VElement
, each one wrapping its org.w3c.dom
counterpart. The example does not show, but you can of course edit attributes in a fluent way.
W3C Dom level API is still available through methods VDocument#getW3cDocument
and VElement#getW3cElement
.
Tips: With IntelliJ you can adjust indentation right and left using tab
and shift+tab
.
Maven:
<dependency>
<groupId>com.github.djeang</groupId>
<artifactId>vincer-dom</artifactId>
<version>1.4.0</version>
</dependency>
Jeka:
.add("com.github.djeang:vincer-dom:1.4.0")
Vincer-dom is build with Jeka.
If you don't use Intellij Plugin for Jeka, right after fetching this project, execute:
- For Eclipse:
./jekaw eclipse#files
- For IntelliJ:
./jekaw intellij#iml
Note that you don't need to install Jeka on your machine.
./jeka project: pack
Just create a new release on project github page.
The configuration lies in jeka.properties file.
The publication is done via a github action defining the required environment variables :
env:
jeka.repos.publish.username: ${{ secrets.OSSRH_USER }}
jeka.repos.publish.password: ${{ secrets.OSSRH_PWD }}
jeka.gpg.secret-key: ${{ secrets.GPG_SECRET_KEY}}
jeka.gpg.passphrase: ${{ secrets.GPG_PASSPHRASE }}
The content of secrets.GPG_SECRET_KEY
has been obtained by executing : gpg --export-secret-key --armor my-key-name
.
Just execute jeka maven: publish
to publish on Maven Central.
- Add namespace support
Enjoy !
Icon made by https://www.stockio.com/free-icon/medical-icons-syringe