This post covers how to perform Mail Merge operations in MS Word documents using Java. By the end of this article, you will learn how to create Mail Merge templates and execute Mail Merge programmatically.

About Mail Merge

Mail Merge is a convenient way of generating letters, envelopes, invoices, reports, and other types of documents dynamically. Using Mail Merge, you create a template file containing the merge fields and then populate those fields using the data in a data source. Let’s suppose you have to send a letter to 20 different people and you only need to change the name and the address of the receivers on each copy. In this case, you can create a Mail Merge template for the letter and then generate 20 letters by populating the name and address fields dynamically.

Java Mail Merge API - Free Download

Aspose.Words for Java is a well-known word processing API that lets you create various types of documents from scratch. The API provides built-in Mail Merge features that allow you to dynamically generate documents using templates and data sources. Aspose.Words for Java can be downloaded as JAR or installed within the Maven-based applications.

<repository>
    <id>AsposeJavaAPI</id>
    <name>Aspose Java API</name>
    <url>https://repository.aspose.com/repo/</url>
</repository>
<dependency>
    <groupId>com.aspose</groupId>
    <artifactId>aspose-words</artifactId>
    <version>20.11</version>
    <classifier>jdk17</classifier>
</dependency>

Data Sources for Mail Merge

The data in the Mail Merge could be fetched from any data source such as JSON, XML, spreadsheet, or a database.

Create Template for Mail Merge in MS Word

The template being used in Mail Merge can be a simple Word document (i.e DOCX) and it doesn’t need to be in a template format. The template document contains the merge fields that are populated with data when Mail Merge is executed. The following are the steps of how to prepare a Mail Merge template using MS Word.

  • Create a new document in MS Word.
  • Place the cursor where you want to add a merge field.
  • From the Insert menu select the Field option.
  • From the Field names list, select MergeField.
  • Enter a name for the merge field in the Field name and press OK.
  • Save the document as DOCX.

The following is the screenshot of the sample template document.

mail merge word template

Create Mail Merge Template using Java

You can also generate the Mail Merge template programmatically. The following are the steps for it.

The following code sample shows how to create a Mail Merge template using Java.

// Create document builder
DocumentBuilder builder = new DocumentBuilder();
// Insert a text input field the unique name of this field is "Hello", the other parameters define
// what type of FormField it is, the format of the text, the field result and the maximum text length (0 = no limit)
builder.insertTextInput("TextInput", TextFormFieldType.REGULAR, "", "Hello", 0);
builder.insertField("MERGEFIELD CustomerFirstName \\* MERGEFORMAT");
builder.insertTextInput("TextInput1", TextFormFieldType.REGULAR, "", " ", 0);
builder.insertField("MERGEFIELD CustomerLastName \\* MERGEFORMAT");
builder.insertTextInput("TextInput1", TextFormFieldType.REGULAR, "", " , ", 0);
// Insert a paragraph break into the document
builder.insertParagraph();
// Insert mail body
builder.insertTextInput("TextInput", TextFormFieldType.REGULAR, "", "Thanks for purchasing our ", 0);
builder.insertField("MERGEFIELD ProductName \\* MERGEFORMAT");
builder.insertTextInput("TextInput", TextFormFieldType.REGULAR, "", ", please download your Invoice at ",
0);
builder.insertField("MERGEFIELD InvoiceURL \\* MERGEFORMAT");
builder.insertTextInput("TextInput", TextFormFieldType.REGULAR, "",
". If you have any questions please call ", 0);
builder.insertField("MERGEFIELD Supportphone \\* MERGEFORMAT");
builder.insertTextInput("TextInput", TextFormFieldType.REGULAR, "", ", or email us at ", 0);
builder.insertField("MERGEFIELD SupportEmail \\* MERGEFORMAT");
builder.insertTextInput("TextInput", TextFormFieldType.REGULAR, "", ".", 0);
builder.insertParagraph();
// Insert mail ending
builder.insertTextInput("TextInput", TextFormFieldType.REGULAR, "", "Best regards,", 0);
builder.insertBreak(BreakType.LINE_BREAK);
builder.insertField("MERGEFIELD EmployeeFullname \\* MERGEFORMAT");
builder.insertTextInput("TextInput1", TextFormFieldType.REGULAR, "", " ", 0);
builder.insertField("MERGEFIELD EmployeeDepartment \\* MERGEFORMAT");
// Save document
builder.getDocument().save("document.docx");

Perform Mail Merge in Word Document using Java

Once the template is ready, you can populate the merge fields with data. The following are the steps of performing Mail Merge on a Word template.

The following code sample shows how to perform mail merge in Word documents using Java.

// Include the code for our template
Document doc = new Document();
// Pass the document to document builder
DocumentBuilder builder = new DocumentBuilder(doc);
// Create Merge Fields
builder.insertField(" MERGEFIELD CustomerName ");
builder.insertParagraph();
builder.insertField(" MERGEFIELD Item ");
builder.insertParagraph();
builder.insertField(" MERGEFIELD Quantity ");
// Save the template
builder.getDocument().save("MailMerge.TestTemplate.docx");
// Fill the fields in the document with user data
doc.getMailMerge().execute(new String[] { "CustomerName", "Item", "Quantity" },
new Object[] { "John Doe", "Hawaiian", "2" });
// Save the document
builder.getDocument().save("MailMerge.Simple.docx");
view raw mail-merge.java hosted with ❤ by GitHub

Template

simple mail merge in java

Output

simple mail merge

Perform Mail Merge using XML Data Source

In the previous example, we performed Mail Merge using the Java objects. However, in most of the cases, a data source is used to populate the merge fields. For demonstration, let’s check out how to use an XML data source in Mail Merge. The following are the steps for it.

  • Load the XML data source using the DataSet class.
  • Load the Mail Merge template using Document class.
  • Use the execute function to populate the merge fields using the desired data table in the data source.
  • Save the generated Word document using Document.save(String) method.

The following is the XML data source being used in this example.

<?xml version="1.0" encoding="utf-8"?>
<customers>
<customer Name="John Ben Jan" ID="1" Domain="History" City="Boston"/>
<customer Name="Lisa Lane" ID="2" Domain="Chemistry" City="LA"/>
<customer Name="Dagomir Zits" ID="3" Domain="Heraldry" City="Milwaukee"/>
<customer Name="Sara Careira Santy" ID="4" Domain="IT" City="Miami"/>
</customers>
view raw datasource.xml hosted with ❤ by GitHub

The following code sample shows how to populate the Mail Merge template using the Customer data table in the provided XML data source.

// Create the Dataset and read the XML
DataSet customersDs = new DataSet();
customersDs.readXml("Customers.xml");
// Open a template document
Document doc = new Document("TestFile XML.docx");
// Execute mail merge to fill the template with data from XML using DataTable.
// Note that this class also works with a single repeatable region (and any nested regions).
// To merge multiple regions at the same time from a single XML data source, use the XmlMailMergeDataSet class.
// e.g doc.getMailMerge().executeWithRegions(new XmlMailMergeDataSet(xmlData));
doc.getMailMerge().execute(customersDs.getTables().get("Customer"));
// Save the output document
doc.save("generated-document.docx");

Template

mail merge with XML

Output

mail merge xml

Mail Merge with Regions in Java

In certain cases, you may need to repeat a particular region in the document. For example, you want to display orders placed by each customer in a separate table. In such cases, you can take advantage of the Mail Merge regions. In order to create a region, you can specify the start and the end of the region. As a result, the region will be repeated for each instance of the data during the Mail Merge execution.

The following screenshot shows a template in which the region consists of a table. It starts with «TableStart:Customers» and ends at «TableEnd:Customers».

mail merge region template

The following code sample shows how to create a template with regions and populate it with the data.

// Create document
Document doc = new Document();
DocumentBuilder builder = new DocumentBuilder(doc);
// The start point of mail merge with regions the dataset.
builder.insertField(" MERGEFIELD TableStart:Customers");
// Data from rows of the "CustomerName" column of the "Customers" table will go
// in this MERGEFIELD.
builder.write("Orders for ");
builder.insertField(" MERGEFIELD CustomerName");
builder.write(":");
// Create column headers
builder.startTable();
builder.insertCell();
builder.write("Item");
builder.insertCell();
builder.write("Quantity");
builder.endRow();
// We have a second data table called "Orders", which has a many-to-one
// relationship with "Customers"
// picking up rows with the same CustomerID value.
builder.insertCell();
builder.insertField(" MERGEFIELD TableStart:Orders");
builder.insertField(" MERGEFIELD ItemName");
builder.insertCell();
builder.insertField(" MERGEFIELD Quantity");
builder.insertField(" MERGEFIELD TableEnd:Orders");
builder.endTable();
// The end point of mail merge with regions.
builder.insertField(" MERGEFIELD TableEnd:Customers");
// Pass our dataset to perform mail merge with regions.
DataSet customersAndOrders = CreateDataSet();
doc.getMailMerge().executeWithRegions(customersAndOrders);
// Save the result
doc.save("MailMerge.ExecuteWithRegions.docx");

Output

mail merge region

Create Nested Mail Merge Regions using Java

Another popular scenario in Mail Merge is when you have nested regions. For example, when you have to list the orders and the items in each order then you can use the nested regions. The following image makes the picture more clear about the nested regions.

In the image above, we have the Orders table and the Items table where each record in Items is linked to a record in Orders. Hence, there exists a one-to-many relationship between these two tables. In such cases, Aspose.Words executes the Mail Merge taking care of the relationships defined in the DataSet. For instance, if we have an XML data source then Aspose.Words will either use the schema information or the structure of XML to find out the relationships. Thus, you will not have to handle it manually by yourself and the Document.getMailMerge().executeWithRegions(DataSet) method will work for you (as the previous example).

Apply Custom Formatting on Merge Fields

In order to give you more control over the Mail Merge, Aspose.Words for Java allows you to customize the merge fields during the Mail Merge execution. The setFieldMergingCallback(IFieldMergingCallback) method accepts a class which implements fieldMerging(FieldMergingArgs) and imageFieldMerging(ImageFieldMergingArgs) methods for custom control over the Mail Merge process. The fieldMerging(FieldMergingArgs) event occurs when a merge field is encountered during the Mail Merge execution.

The following is the complete code sample of how to customize the Mail Merge operation and apply formatting to the cells.

public class ApplyCustomFormattingDuringMailMerge {
private static final String dataDir = Utils.getSharedDataDir(ApplyCustomFormattingDuringMailMerge.class) + "MailMerge/";
public static void main(String[] args) throws Exception {
Document doc = new Document(dataDir + "MailMerge.AlternatingRows.doc");
// Add a handler for the MergeField event.
doc.getMailMerge().setFieldMergingCallback(new HandleMergeFieldAlternatingRows());
// Execute mail merge with regions.
DataTable dataTable = getSuppliersDataTable();
doc.getMailMerge().executeWithRegions(dataTable);
doc.save(dataDir + "MailMerge.AlternatingRows Out.doc");
}
/**
* Returns true if the value is odd; false if the value is even.
*/
public static boolean isOdd(int value) throws Exception {
return (value % 2 != 0);
}
/**
* Create DataTable and fill it with data. In real life this DataTable
* should be filled from a database.
*/
private static DataTable getSuppliersDataTable() throws Exception {
java.sql.ResultSet resultSet = createCachedRowSet(new String[]{"CompanyName", "ContactName"});
for (int i = 0; i < 10; i++)
addRow(resultSet, new String[]{"Company " + Integer.toString(i), "Contact " + Integer.toString(i)});
return new DataTable(resultSet, "Suppliers");
}
/**
* A helper method that creates an empty Java disconnected ResultSet with
* the specified columns.
*/
private static ResultSet createCachedRowSet(String[] columnNames) throws Exception {
RowSetMetaDataImpl metaData = new RowSetMetaDataImpl();
metaData.setColumnCount(columnNames.length);
for (int i = 0; i < columnNames.length; i++) {
metaData.setColumnName(i + 1, columnNames[i]);
metaData.setColumnType(i + 1, java.sql.Types.VARCHAR);
}
CachedRowSet rowSet = RowSetProvider.newFactory().createCachedRowSet();
;
rowSet.setMetaData(metaData);
return rowSet;
}
/**
* A helper method that adds a new row with the specified values to a
* disconnected ResultSet.
*/
private static void addRow(ResultSet resultSet, String[] values) throws Exception {
resultSet.moveToInsertRow();
for (int i = 0; i < values.length; i++)
resultSet.updateString(i + 1, values[i]);
resultSet.insertRow();
// This "dance" is needed to add rows to the end of the result set properly.
// If I do something else then rows are either added at the front or the result
// set throws an exception about a deleted row during mail merge.
resultSet.moveToCurrentRow();
resultSet.last();
}
}
class HandleMergeFieldAlternatingRows implements IFieldMergingCallback {
/**
* Called for every merge field encountered in the document. We can either
* return some data to the mail merge engine or do something else with the
* document. In this case we modify cell formatting.
*/
public void fieldMerging(FieldMergingArgs e) throws Exception {
if (mBuilder == null)
mBuilder = new DocumentBuilder(e.getDocument());
// This way we catch the beginning of a new row.
if (e.getFieldName().equals("CompanyName")) {
// Select the color depending on whether the row number is even or odd.
Color rowColor;
if (ApplyCustomFormattingDuringMailMerge.isOdd(mRowIdx))
rowColor = new Color(213, 227, 235);
else
rowColor = new Color(242, 242, 242);
// There is no way to set cell properties for the whole row at the moment,
// so we have to iterate over all cells in the row.
for (int colIdx = 0; colIdx < 4; colIdx++) {
mBuilder.moveToCell(0, mRowIdx, colIdx, 0);
mBuilder.getCellFormat().getShading().setBackgroundPatternColor(rowColor);
}
mRowIdx++;
}
}
public void imageFieldMerging(ImageFieldMergingArgs args) throws Exception {
// Do nothing.
}
private DocumentBuilder mBuilder;
private int mRowIdx;
}

Conclusion

Mail Merge provides you with a wide range of features to generate MS Word documents dynamically. You can generate simple as well as complex reports based on the data in databases or other data sources. In this article, you have seen how to implement the Mail Merge features programmatically using Java. You can learn more about the Java Mail Merge API using the documentation.

See Also