{{announcement.body}}
{{announcement.title}}

Vincer-Dom and Modern XML/DOM Manipulation

DZone 's Guide to

Vincer-Dom and Modern XML/DOM Manipulation

How to manipulate an XML DOM programmatically with a truly fluent API.

· Java Zone ·
Free Resource

Writing or editing DOM-XML in Java (or another language) has never been natural or straightforward. The 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 to offer a powerful fresh new style API.

To give a concrete idea, the following code:

  1. Reads a pom.xml file (a Maven XML file that most of Java developers know)
  2. Adds 2 dependencies
  3. Removes all dependencies having test scope
  4. Modifies distribution repository, creating potential missing elements.
Java
 




x


 
1
public void editMavenPom() {
2
    InputStream stream = EditTest.class.getResourceAsStream("sample-pom.xml");
3
    VDocument doc = VDocument.parse(stream)
4
      .root()
5
        .get("dependencies")
6
            .add("dependency")
7
                .add("groupId").text("com.github.djeang").__
8
                .add("artifactId").text("vincer-dom").__
9
                .add("version").text("0.1-SNAPSHOT").__.__
10
            .add("dependency")
11
                .add("groupId").text("org.junit.jupiter").__
12
                .add("artifactId").text("junit-jupiter-engine").__
13
                .add("version").text("5.4.0").__
14
                .add("scope").text("test").__.__
15
            .apply(this::removeTests).__
16
        .get("distributionManagement")    
17
            .get("repository")      
18
                .get("id").make().text("My repo id").__  // make() creates absent elements
19
                .get("name").make().text("My repo name").__
20
                .get("url").make().text("http://myserver::8081").__.__.__.__;
21
    doc.print(System.out);
22
}
23
 
          
24
private void removeTests(VElement<?> dependencies) {
25
    dependencies.getAll("dependency").stream()
26
        .filter(dependency -> "test".equals(dependency.get("scope").getText()))
27
        .forEach(VElement::remove);
28
}



The complete Dom manipulation has been expressed in a single chained statement, reflecting the tree structure of the manipulated data.

If you don't know about Parent-Chaining Pattern,  .__ returns the parent of the current element. This pattern really shines when manipulating tree like structures.

Using JDom (a library embracing method chaining though), to achieve exactly the same, the best we can do is:

Java
 




x





1
public void editMavenPomWithJdom() {
2
    InputStream stream = JdomEditTest.class.getResourceAsStream("sample-pom.xml");
3
    final Document document;
4
    try {
5
      DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
6
      org.w3c.dom.Document w3cDocument = builder.parse(stream);
7
      document = new DOMBuilder().build(w3cDocument);
8
    }
9
    catch (Exception e) {
10
      throw new RuntimeException(e);
11
    }
12
    Element root = document.getRootElement();
13
    Element dependencies = root.getChild("dependencies");
14
    dependencies.addContent
15
      (new Element("dependency")
16
       .addContent(new Element("group").setText("com.github.djeang"))
17
       .addContent(new Element("artifactId").setText("vincer-dom"))
18
       .addContent(new Element("version").setText("0.1-SNAPSHOT"))
19
      );
20
    dependencies.addContent
21
      (new Element("dependency")
22
       .addContent(new Element("group").setText("org.junit.jupiter"))
23
       .addContent(new Element("artifactId").setText("unit-jupiter-engine"))
24
       .addContent(new Element("version").setText("5.4.0"))
25
       .addContent(new Element("scope").setText("test"))
26
      );
27
    removeTests(dependencies);
28
    Element distributionManagement = getOrCreate(root, "distributionManagement");
29
    Element repository = getOrCreate(distributionManagement, "repository");
30
    getOrCreate(repository, "id").setText("My repo id");
31
    getOrCreate(repository, "name").setText("My repo name");
32
    getOrCreate(repository, "url").setText("http://myserver::8081");
33
    final XMLOutputter xmlOutputter = new XMLOutputter();
34
    try {
35
      xmlOutputter.output(document, System.out);
36
    } catch (IOException e) {
37
      throw new RuntimeException(e);
38
    }
39
  }
40
 
          
41
static Element getOrCreate(Element parent, String name) {
42
    Element element = parent.getChild(name);
43
    if (element == null) {
44
      element = new Element(name);
45
      parent.addContent(element);
46
    }
47
    return element;
48
}
49
 
          
50
private void removeTests(Element dependencies) {
51
    for (ListIterator<Element> it = dependencies.getChildren().listIterator();it.hasNext();) {
52
      Element dependency = it.next();
53
      if ("test".equals(dependency.getChildText("scope"))) {
54
        it.remove();
55
      }
56
    }
57
}



As you can see, Vincer-Dom saves a lot of coding effort while getting code much more readable.

Moreover, the API is very thin as it consists of only 2 classes : VDocument and VElement, each one wrapping its org.w3c.dom counterpart. 

Hope this article makes you attract to use this library and gives you new fresh ideas to design your next APIs.

Topics:
design pattens, fluent design style, fluent interface, java, tutorial, xml api integration

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}