Based on some of the old threads I have created my own implementation of IMailMergeDataSource to parse json and use that for the mail merge. However, what I’m not sure how to get dynamic rows of a table populated in the merged document.
I have attached the implementation and the template.
Here is the code which generates the merged document using the template
public byte[] mergeWordDocWithRegions(byte[] templateStream, IMailMergeDataSource ds) {
ByteArrayOutputStream docOutStream = new ByteArrayOutputStream();
try {
Document doc = new Document(new ByteArrayInputStream(templateStream));
doc.getMailMerge().setUseNonMergeFields(true);
// This is to cleanup the mergeFields in the word template for which data has not been filled in
doc.getMailMerge().setCleanupOptions(MailMergeCleanupOptions.REMOVE_UNUSED_FIELDS);
// Execute the mail merge.
//doc.getMailMerge().execute(fieldNames, fieldValues);
doc.getMailMerge().execute(ds);
LogHelper.info(LOG, "Mail merge performed successfully.");
doc.save(docOutStream, SaveFormat.PDF);
LogHelper.info(LOG, "Survey has been saved as PDF format");
} catch (Exception e) {
LogHelper.error(LOG, "Exception stack" + ExceptionUtils.getStackTrace(e));
}
return docOutStream.toByteArray();
}
Thanks for your quick response. I tried to do this. But may be I’m missing something here.
The JSON Data is as below;
“other”: [
{
“pc”: “IDT”,
“ni”: “test1”
},
{
“pc”: “FBT”,
“ni”: “test2”
}
],
I need to put this in the tabular format as below as rows.
IDT test
FBT test2
I tried to using mail merge field in Word template using following
«TableStart:other»«pc» «ni»«TableEnd:other»
It just prints the same in the merged document. It doesn’t replace it. In fact, I don’t even get the field names when I try to do e.getFieldName() inside fieldMerging() method.
It’ll be of great help if you can point me to an working example of how to populate data in a table using mail merge fields.
I tried to use LINQ Reporting Engine as per below link. But still I can’t get through it.
using tags such as
<<foreach [
c in other
]>><<[c.pc]>> <<[c.ni]>><>
Could you please share some more detail about your requirement along with following detail?
Your input Word document.
Please attach the output Word file that shows the undesired behavior.
Please attach the expected output Word file that shows the desired behavior.
Please create a simple Java application ( source code without compilation errors ) that helps us to reproduce your problem on our end and attach it here for testing.
As soon as you get these pieces of information ready, we will start investigation into your issue and provide you more information. Thanks for your cooperation.
PS: To attach these resources, please zip and upload them.
Please see the attached zip file. I have added following;
Input Word Document - Template.docx
output word file that shows the undesired behaviour - MergedDocument.pdf
simple Java application - Source files are there in the zip file. Please run DocumentConverterControllerTest unit test which will generate the merged document in …\Sample\converter\src\test\resources
Expected Output - Data is coming but table formatting is not done correctly.
Now the point is if you look at FieldMergeCallback class, I have done a bit of coding to create a table and not used Row row = (Row)e.getField().getStart().getAncestor(NodeType.ROW); provided by you.
Since the data is in json format (attached in the zip file - data.json), I had to do this. So I’m just wondering if there is any better way to handle this.
Because I’ll have various tables in my json. All of them will have different names for the table. So I can’t hard code that in the code.
I added TableStart:other and TableEnd:other as 2 merge fields in the word template one appearing in the first cell and the other in 2nd.
I did invoke doc.getMailMerge().executeWithRegions(ds).
For some reason it doesn’t call getChildDataSource() method in the data source class I have created. Based on the documentation - IMailMergeDataSource | Aspose.Words for Java, I believe the engine must call this method. But it doesn’t.
So I’m not sure what I need to do to make sure engine invokes this method.
Template changes
OTHER CLASS NAME INSURED
«TableStart:other»«other.pc» «other.ni»«TableEnd:other»
Added another class called JSONMailMergeDataSet which implements IMailMergeDataSourceRoot
using
doc.getMailMerge().execute(ds);
doc.getMailMerge().executeWithRegions(dataSet);
ds - JSONObjectMailMergeDataSource for the same json data
dataSet - JSONMailMergeDataSet for the same json data
After doing this it’s putting only the last row in the generated document. Please see the attached documents. Also, it’s not well formatted. Can you please check this and let me know what I’m not doing right?Converter.zip (180.7 KB)
You are creating new table in fieldMerging method. This is the reason you are getting different table’s width. Please clone the table’s row as shown in following code. In this following method you can write the field’s value according to your requirement. Hope this helps you.
The Aspose.Words mail merge engine invokes IMailMergeDataSource.GetChildDataSource method when it encounters a beginning of a nested mail merge region.
I was able to finally get this sorted out using following code.
private void mergeTableData(FieldMergingArgs e) throws Exception {
Row row = (Row) e.getField().getStart().getAncestor(NodeType.ROW);
if (row != null) {
row.getRowFormat().setHeightRule(HeightRule.AUTO);
Row toBeCloned = (Row) row.deepClone(true);
if (e.getFieldName().startsWith("TABLE_")){
JsonElement table = (JsonElement) e.getFieldValue();
if (table.isJsonArray()) {
JsonArray tableData = table.getAsJsonArray();
DocumentBuilder builder = new DocumentBuilder(e.getDocument());
for (int i = 0; i < tableData.size(); i++) {
JsonElement currentRow = tableData.get(i);
Set<Entry<String, JsonElement>> rowData = currentRow.getAsJsonObject().entrySet();
int cellIndex = 0;
for (Entry<String, JsonElement> cell : rowData) {
if (cellIndex == 0) {
builder.moveToMergeField(e.getFieldName());
} else {
builder.moveTo(row.getCells().get(cellIndex).getFirstParagraph());
}
builder.write(cell.getValue().getAsString());
cellIndex++;
}
// Check if more rows there; if yes then clone the template row
if (i < tableData.size() - 1) {
Row newRow = (Row) toBeCloned.deepClone(true);
row.getParentTable().getRows().add(newRow);
row = newRow;
}
}
}
}
e.setText("");
}
}
However, I have a question here. Is this the good approach or there is a better approach like using regions and applying TableStart:. If using regions is a better approach, it will be good to see a working example. I’m sure lot of Aspose users will be using JSON data. So I’m pretty sure it will be beneficial to all of us to look at a working example for Json data source.
Please note that Aspose.Words does not provide API to process JSON data/string. For such cases, you need to implement IMailMergeDataSource interface and work with JSON according to your requirement.
Yes, the approach using mail merge with region is better in this case. You can also parse your JSON string using Java API, create com.aspose.words.net.System.Data.DataTable object and pass it to MailMerge.ExecuteWithRegions.
Sorry for the late response. Thanks for the recommendation. Just one question - do you have plans to support json in the future? I mean that’s an industry standard now. So will be good to see if it can be supported by the library itself.
Currently, we do not have plans to implement this feature. Did you try this solution?
If you still want us to implement this feature, please ZIP and attach your complete detail of your use case along with JSON samples, input and expected output documents. We will then log it in our issue tracking system according to your requirement.
I haven’t tried this approach as I’m able to work with IMailMergeDataSource approach you gave me in the first instance. But I strongly think, there should be in built support in Aspose.words for JSON data as that’s the standard industry follows anyway.
Some of the samples I provided in this thread has cases. But I’ll try to get all of them in one place and share with you soon.
Please share complete detail of your use cases along with JSON samples, input and expected output documents. We will then log it in our issue tracking system according to your requirement.