Custom Implementation using the IMailMergeDataSource in template should be able to pass any pojo (simple or hierarchical)

Hello Aspose Team,

We are creating a POC to evaluate Aspose and other library to build a solution arround mailmerge.

Please find attached input and output template with the working case.

User Name : -«tablestart:Users»«Name»
Department Name : - «tablestart:Departments»«Name»
Addresss:- «tablestart:Addreses»
Name :-«Name»
Street :- «Street»
«tableend:Addreses»
«tableend:Departments»
«tableend:Users»

And Custom IMailMergeDataSource Source indent code is as follows :

import com.aspose.words.IMailMergeDataSource;
import java.util.List;

public class UserMailMergeDataSource implements IMailMergeDataSource {

    public UserMailMergeDataSource(List items, String tableName)
    {
        mItems = items;
        mTableName = tableName;
        // When the data source is initialized, it must be positioned before the first record.

        mRecordIndex = -1;

    }

    ///
    /// The name of the data source. Used by Aspose.Words only when executing mail merge with repeatable regions.
    ///
    public String getTableName()
    {
        return mTableName;
    }

    ///
    /// Aspose.Words call this to get a value for every data field.
    ///
    public boolean getValue(String fieldName, Object[] fieldValue)
    {
        System.out.println("Field Name::Custom User :::::" + fieldName);
        fieldValue[0] = getFieldValue(fieldName);

        return fieldValue[0] != null;
    }

    public IMailMergeDataSource getChildDataSource(String childName) throws Exception {
        System.out.println("childName:" + childName);
        Object childData = getFieldValue(childName);
        System.out.println("childData::" + childData);

        if (childData != null && List.class.isInstance(childData)) {

            // List departments = (List) getFieldValue(childName);
            return new UserMailMergeDataSource((List) childData, childName);
        }

        return null;
    }

    /// 
    /// A standard implementation for moving to a next record in a collection.
    /// 
    public boolean moveNext()
    {
        if (isEof())
            return false;

        mRecordIndex++;
        return (!isEof());
    }

    private boolean isEof()
    {
        return (mRecordIndex >= mItems.size());
    }

    private Object getFieldValue(String fieldName)
    {
        // Get value of the appropriate field usign reflection.
        // System.out.println("mRecordIndex::::::" + mRecordIndex);
        Object item = mItems.get(mRecordIndex);
        Class itemClass = item.getClass();
        // System.out.println("item:::class:::" + item.getClass().getName());
        java.lang.reflect.Field field = null;
        try {
            field = itemClass.getField(fieldName);
            // field.get(itemClass.getName().equals())
            System.out.println("field::::::" + field);
        } catch (Exception ex) {
        }

        if (field != null)
        {
            try {
                return field.get(item);
            } catch (Exception ex) {
            }
            return true;
        }
        return null;
    }

    private List mItems;
    private int mRecordIndex;
    private String mTableName;
}

Using following Pojo:-

  1. User.Java
public class User
{

    public List Departments = new ArrayList();

    public List getDepartments() {
        return Departments;
    }

    public void setDepartments(List departments) {
        Departments = departments;
    }

    public String getName() {
        return Name;
    }

    public void setName(String name) {
        Name = name;
    }

    public String Name;
}
  1. Department.java
public class Department
{
    public List Addreses = new ArrayList();

    public List getAddreses() {
        return Addreses;
    }

    public void setAddreses(List addreses) {
        Addreses = addreses;
    }

    public String Name;

    public void setName(String name) {
        Name = name;
    }
}

  1. Address.java
public class Address
{
    public String Name;
    public String Street;

    public String getName() {
        return Name;
    }

    public void setName(String name) {
        Name = name;
    }

    public String getStreet() {
        return Street;
    }

    public void setStreet(String street) {
        Street = street;
    }
}

How can we implement custom code using following template:-

«user[0].department[3].address[0].name»

*We are able to accomplish most of the feature that we need in POC but following use case not seems to be working with input template.

«user[0].department[3].address[0].name»*

Could anyone in your team let us know that, Is IMailMergeDataSource really supported with above format to populate the data , if yes then can you please provide the sample code ?

Thanks in advance.
Regards,
Dilip

Hi Dilip,

Thanks for your inquiry. We are checking with this scenario and will get back to you soon.

Best regards,

Hi Awais,

Thanks for your response.
Kindly let us know whenever you will get something on it.
We are exploring this libraries for the document conversion and we have bulk documents to process, So this feature we required to build a generic solution with multiple test cases.

Thanks in advance.

Thanks,
Dilip

Hi Dilip,

Thanks for being patient.

In your templte document, “Users” is the outermost mail merge region. Therefore, it can repeat itself and conatain multiple records of “Departments” region. “Departments” is a nested region. For a particular “Users” record, it can repeat itself and contain multiple records of “Addresses” region. “Addreses” is nested inside “Departments” and can repeat itself. I believe, using the following code, you can build data source from the provided pojo classes and pass it to mail merge engine of Aspose.Words to obtain the desired output:

ArrayList<User> userRecords = new ArrayList<User>();
ArrayList<Department> departmentRecords = new ArrayList<Department>();
ArrayList<Address> addressRecords1 = new ArrayList<Address>();
ArrayList<Address> addressRecords2 = new ArrayList<Address>();
ArrayList<Address> addressRecords3 = new ArrayList<Address>();
Address address = new Address();
address.setName("dep1 Name1");
address.setStreet("dep1 22");
addressRecords1.add(address);
address = new Address();
address.setName("dep1 Name2");
address.setStreet("dep1 34");
addressRecords1.add(address);
address = new Address();
address.setName("dep1 Name3");
address.setStreet("dep1 45");
addressRecords1.add(address);
address = new Address();
address.setName("dep2 Name1");
address.setStreet("dep2 45");
addressRecords2.add(address);
address = new Address();
address.setName("dep2 Name2");
address.setStreet("dep2 45");
addressRecords2.add(address);
address = new Address();
address.setName("dep3 Name1");
address.setStreet("dep3 45");
addressRecords3.add(address);
Department department = new Department();
department.setName("IT Develpoment");
department.setAddreses(addressRecords1);
departmentRecords.add(department);
department = new Department();
department.setName("IT Supprot");
department.setAddreses(addressRecords2);
departmentRecords.add(department);
department = new Department();
department.setName("Operation");
department.setAddreses(addressRecords3);
departmentRecords.add(department);
User user = new User();
user.setName("User1");
user.setDepartments(departmentRecords);
userRecords.add(user);
Document doc = new Document(getMyDir() + "in.doc");
UserMailMergeDataSource ds = new UserMailMergeDataSource(userRecords, "Users");
doc.getMailMerge().executeWithRegions(ds);
doc.save(getMyDir() + "out.doc");

I hope, this helps.

Best regards,

Thanks for above code.

I am able to iterate the complete list data using the tablestart and tableend .
But my requirement is , I wants to display only specific records from the list.
like addressRecord1 of Department “IT development” for User1 , Not the complete list.

example- Name and Street for the department “IT Development” which User1 have.
Template Input :-

Name :- «Users[0].Departments[0].Addreses[0].Name»

Street :- «Users[0].Departments[0].Addreses[0].Street»

and Template output should be like:-

Name :- dep1 Name1

Street :- dep1 22

So above code is not able to populate the expected data.
Also if I am putting wrong value in input template then please let me know the correct way to give input in to template to access the particular value from the list.

Kindly let me know if my requirement is still not clear.

Thanks in advance.

Regards,
Dilip

Hi Dilip,

Thanks for your inquiry. Why are you placing nested mail merge regions in your template document if you only need to merge them with single values? I suppose you can implement IFieldMergingCallback interface if you want to control how data is inserted into merge fields during a mail merge operation. You could also use normal merge fields (see attached template) and use simple MailMerge.Execute Method (String[], Object[]) method to achieve this. Please refer to the following articles for more details:
https://docs.aspose.com/words/java/mail-merge-and-reporting/

Best regards,