Apache Commons Digester – How to Substitute Variables in Configuration XML

The Apache Commons Digester is used for parsing a XML file and converting into java bean objects.

In this post, we are going to see how we can use the digester with an example.

Refer below my book configuration XML file. My objective is to parse this file, substitute the variables present in this file by environmental/hardcoded values and get the book config bean objects.


<?xml version="1.0"?>

Refer below my Book Config Bean class. This class should have the corresponding XML fields and then getter/setter methods.


package com;

import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;

import java.io.Serializable;

public class BookConfigBean implements Serializable {

    private static final long serialVersionUID = -2405094408469829853L;

    private String title;

    private String author;

    private String price;

    public String getTitle() {
        return title;

    public void setTitle(String title) {
        this.title = title;

    public String getAuthor() {
        return author;
    public void setAuthor(String author) {
        this.author = author;

    public String getPrice() {
        return price;

    public void setPrice(String price) {
        this.price = price;
    public String toString() {
        return ToStringBuilder.reflectionToString(this);

    public boolean equals(Object o) {
        return EqualsBuilder.reflectionEquals(this, o);

    public int hashCode() {
        return HashCodeBuilder.reflectionHashCode(this);

Refer below the BookConfigurationDigester class which has the parsing and variable substitution logic.

Refer below to know the logic behind in parsing the book XML file(load() method).

1. The first step is to create a Source Map and populate the key/value pairs into it. In this example, I have hardcoded all the values, but you can also retrieve the configuration from Environmental variables. Keep in mind that the format of the Source Map is .

2. Then, Create an instance of MultiVariableExpander and add the source map and symbol to it. Here we use ‘$’ as MARKER symbol.

3. Then create an instance of VariableSubstitutor and pass the MultiVariableExpander instance to it

4. Then create an instance of Digester and pass the VariableSubstitutor instance to it.

5. Parse the book XML file and use VariableSubstitutor object for doing the variable substitution.


package com;

import org.apache.commons.digester3.Digester;
import org.apache.commons.digester3.Substitutor;
import org.apache.commons.digester3.substitution.MultiVariableExpander;
import org.apache.commons.digester3.substitution.VariableSubstitutor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class BookConfigurationDigester {

    private static Logger LOGGER = LoggerFactory.getLogger(BookConfigurationDigester.class);

    private static final String MARKER = "$";

    private List bookConfigBeans = new ArrayList();

    public void addBookConfigElement(BookConfigBean bean) {

    public void load() {

        Map sourceMap = new HashMap();
        sourceMap.put("BOOK1_PRICE", "$10");
        sourceMap.put("BOOK2_PRICE", "$10");
        sourceMap.put("BOOK3_PRICE", "$20");
        sourceMap.put("BOOK4_PRICE", "$10");
        MultiVariableExpander mvExpander = new MultiVariableExpander();
        mvExpander.addSource(MARKER, sourceMap);
        Substitutor substitutor = new VariableSubstitutor(mvExpander);
        Digester digester = new Digester();
        try (InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("book.xml")) {
            digester.addObjectCreate("books/book", BookConfigBean.class);
            digester.addSetNext("books/book", "addBookConfigElement");
            digester.addCallMethod("books/book/title", "setTitle", 0);
            digester.addCallMethod("books/book/author", "setAuthor", 0);
            digester.addCallMethod("books/book/price", "setPrice", 0);
        catch (Exception e) {
            LOGGER.error("Error parsing configuration xml", e);
            throw new IllegalArgumentException("Error parsing configuration xml", e);
        finally {

    public List getBookConfigBeans() {
        return bookConfigBeans;

Here is my Junit test class.


package com;

import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;

public class BookConfigurationDigesterTest {

    private static Logger LOGGER = LoggerFactory.getLogger(BookConfigurationDigester.class);

    public void testBookConfiguration() {
        BookConfigurationDigester  bookConfigurationDigester = new BookConfigurationDigester();
        List configBeanList = bookConfigurationDigester.getBookConfigBeans();
        assertEquals(4 , configBeanList.size());

Refer the output below.

2017-10-18T11:57:04.967-0400 [main] DEBUG c.BookConfigurationDigester - configBeanList:[com.BookConfigBean@1a968a59[title=Book1,author=Author1,price=$10], com.BookConfigBean@204f30ec[title=Book2,author=Author2,price=$10], com.BookConfigBean@e25b2fe[title=Book3,author=Author3,price=$20], com.BookConfigBean@754ba872[title=Book4,author=Author4,price=$10]]

Refer the code @ xml-digester