Populate Mustache Template with JSON Data in Ruby with Aspose.Words REST API

Hi,

I am trying to work on a template to use mustache fields with looping and providing the data in JSON format in Ruby. For test purposes, I tried using this template that is there in your GitHub repo and used data as which I made by trying to replicate the XMLdata mentioned in your docs.

{
  "Order": [
    {
      "Address": {
        "Number": "23",
        "Street": "Nelson Street",
        "Suburb": "Howick",
        "City": "Auckland"
      },
      "PhoneNumber": "543 1234",
      "Date": "03/01/2010",
      "Total": "14.00",
      "Item": [
        {
          "Name": "BBQ Chicken Pizza",
          "Price": "6.00",
          "Quantity": "1",
          "ItemTotal": "6.00"
        },
        {
          "Name": "1.5 Litre Coke",
          "Price": "4.00",
          "Quantity": "2",
          "ItemTotal": "8.00"
        }
      ]
    },
    {
      "Address": {
        "Number": "10",
        "Street": "Parkville Avenue",
        "Suburb": "Pakuranga",
        "City": "Auckland"
      },
      "PhoneNumber": "548 7342",
      "Date": "05/03/2010",
      "Total": "6.00",
      "Item": [
        {
          "Name": "Hawaiian Pizza",
          "Price": "4.00",
          "Quantity": "1",
          "ItemTotal": "4.00"
        },
        {
          "Name": "Fries",
          "Price": "1.00",
          "Quantity": "2",
          "ItemTotal": "2.00"
        }
      ]
    }
  ]
}

OR

{
  "Orders": {
    "Order": [
      {
        "Address": {
          "Number": "23",
          "Street": "Nelson Street",
          "Suburb": "Howick",
          "City": "Auckland"
        },
        "PhoneNumber": "543 1234",
        "Date": "03/01/2010",
        "Total": "14.00",
        "Item": [
          {
            "Name": "BBQ Chicken Pizza",
            "Price": "6.00",
            "Quantity": "1",
            "ItemTotal": "6.00"
          },
          {
            "Name": "1.5 Litre Coke",
            "Price": "4.00",
            "Quantity": "2",
            "ItemTotal": "8.00"
          }
        ]
      },
      {
        "Address": {
          "Number": "10",
          "Street": "Parkville Avenue",
          "Suburb": "Pakuranga",
          "City": "Auckland"
        },
        "PhoneNumber": "548 7342",
        "Date": "05/03/2010",
        "Total": "6.00",
        "Item": [
          {
            "Name": "Hawaiian Pizza",
            "Price": "4.00",
            "Quantity": "1",
            "ItemTotal": "4.00"
          },
          {
            "Name": "Fries",
            "Price": "1.00",
            "Quantity": "2",
            "ItemTotal": "2.00"
          }
        ]
      }
    ]
  }
}

Both of which didn’t work and I got AsposeWordsCloud::ApiError (Internal Server Error) for them. Let me know the issue. Also, I am eventually trying to use images in a loop but once I understand this and make this work, I will ask that if needed.

Thanks
Nikhil

Also, in the docs, the output template mentioned here in your docs under

Case 2 : Mustache Template

does not match the input template. Please correct that as well.

@nikhilgoyal2205

I have tested the scenario using your second set of JSON data without any issue. Please note when you need to use data with regions then you need to set the with_regions property to true.TestExecuteTemplate_out.zip (43.6 KB)

require 'aspose_words_cloud'

class Document

  include AsposeWordsCloud

  # Get App key and App SID from https://dashboard.aspose.cloud/
  APP_KEY = "xxxxxxxxxxxxxxxxxxxxxxxx"
  APP_SID = "xxxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx"
  FILE_PATH = "C:/Temp/MailMergeSamples/"

  def initialize
    AsposeWordsCloud.configure do |config|
      config.client_data['ClientId'] = APP_SID
      config.client_data['ClientSecret'] = APP_KEY
	  end
    @words_api = WordsApi.new
  end

  # MailMerge
  def execute_mailmerge
      local_document_file = 'TestExecuteTemplate.doc'
      remote_file_name = 'TestExecuteTemplate.doc'
	  remote_file_output = 'TestExecuteTemplate_out.doc'
      local_data_file = File.read(FILE_PATH + 'TestExecuteTemplateData.json')

      request_file_content = File.open(FILE_PATH+ local_document_file)
      request_upload = UploadFileRequest.new(file_content: request_file_content, path: remote_file_name)
      @words_api.upload_file(request_upload)

      request = ExecuteMailMergeRequest.new(name: remote_file_name, data: local_data_file, with_regions: true, dest_file_name: remote_file_output)

      result = @words_api.execute_mail_merge(request)
end
end

@nikhilgoyal2205

We are sorry for the inconvenience. We have logged a ticket(WORDSCLOUD-1779) to fix the issue as soon as possible. Meanwhile, please use withRegions parameter as per your template structure.

Hi @tilal.ahmad,

Thanks for the reply. I am not getting the result, still the same API error. Also, I want to execute the request as ExecuteMailMergeOnlineRequest. I am pasting the code of what I am trying. Let me know what I am doing wrong.

data = {"Orders"=>{"Order"=>[{"Address"=>{"Number"=>"23", "Street"=>"Nelson Street", "Suburb"=>"Howick", "City"=>"Auckland"}, "PhoneNumber"=>"543 1234", "Date"=>"03/01/2010", "Total"=>"14.00", "Item"=>[{"Name"=>"BBQ Chicken Pizza", "Price"=>"6.00", "Quantity"=>"1", "ItemTotal"=>"6.00"}, {"Name"=>"1.5 Litre Coke", "Price"=>"4.00", "Quantity"=>"2", "ItemTotal"=>"8.00"}]}, {"Address"=>{"Number"=>"10", "Street"=>"Parkville Avenue", "Suburb"=>"Pakuranga", "City"=>"Auckland"}, "PhoneNumber"=>"548 7342", "Date"=>"05/03/2010", "Total"=>"6.00", "Item"=>[{"Name"=>"Hawaiian Pizza", "Price"=>"4.00", "Quantity"=>"1", "ItemTotal"=>"4.00"}, {"Name"=>"Fries", "Price"=>"1.00", "Quantity"=>"2", "ItemTotal"=>"2.00"}]}]}}
words_api = AsposeWordsCloud::WordsApi.new #Assume clientID and secret configured
template = File.open('aspose_docs/TestExecuteTemplate.doc')
mail_merge_request = AsposeWordsCloud::ExecuteMailMergeOnlineRequest.new(template: template, data: data, with_regions: true)
result = words_api.execute_mail_merge(mail_merge_request)

Also, I had a question as to why we need to specify Orders in the context json data when we don’t have Orders as a field as such in the template. What if there were some fields before the Order field loop, we will also then require a parent node like Orders? Since we are not also, specifying Items as a parent node before the Item field loop

@nikhilgoyal2205

Please note you are passing ExecuteMailMergeOnlineRequest to execute_mail_merge API method instead of execute_mail_merge_online API method. Please update your code as follows. It will resolve the issue.

request = ExecuteMailMergeOnlineRequest.new(template: request_template, data: request_data, with_regions: true)

result = @words_api.execute_mail_merge_online(request)

@tilal.ahmad
Yeah, I am sorry about that. But even when I am using the execute_mail_merge_online, I am getting the same error. Can you try on your side?

@tilal.ahmad
I have got it working. I had to pass data.to_json as the parameter.
Thanks for your help. Please try to answer the second part of my above post regarding the need for Orders as a root node in the data.

Also, I will now start playing with mustache templates with images, if I have issue, I will reach out.

Edit: @tilal.ahmad it works without Orders as well FYI.

@tilal.ahmad
I had a question: Can we use normal mail merge fields with mustache fields? If not then is there any way to use loops in normal mail merge?

@nikhilgoyal2205

Yes, in this case you can remove Orders field form JSON data as it is not used in the template.

If there were some fields before ‘Order’ field and you wanted to use them in a loop then you would need Orders or any other field name as a parent node for the purpose.

@nikhilgoyal2205

If you want to use normal mail merge fields, then TableStart and TableEnd tags are used for loops in the template that is called mail merge with regions. Please check the following documentation for more details. Hopefully, it will help you to create a template.

@tilal.ahmad
Ok, I will try it. Though, it does not answer my primary question Can we use normal mail merge fields with mustache fields?. Because I tried and it didn’t work.

{{#foreach Order}}

Invoice
{{Date}}

To:
{{Address.Number}} {{Address.Street}}
{{Address.Suburb}}
{{Address.City}}
{{PhoneNumber}}

Items Ordered:
{{#foreach Item}}
Item Name     Price        Quantity	        Sub Total
{{Name}}	  ${{Price}}   {{Quantity}}	    ${{ItemTotal}}
{{/foreach Item}}
		                     Total Cost:	${{Total}}

Thank you for your purchase

{{/foreach Order}}

{{Photo}}

«Caption»

Basically, I just added 2 extra fields Photo(mustache) and Caption(normal mail merge) to the existing template.
In this case, both, the Photo and Caption fields did not get populated.
Obviously, I added values for them in the JSON data.

@nikhilgoyal2205

I have tried both the mail merge field and mustache field in a template and it is working fine at my end. However, I wonder why you are mixing these. It would be better to use the same type of fields in a template to avoid any confusion.TestExecuteTemplatemix.zip (46.3 KB)

Secondly Aspose.Words API allows inserting images on a mail merge field. Please check the sample template and data from the following documentation article for details.

@tilal.ahmad
I don’t know why it’s not working for me, maybe I will post my data and template(which I have posted already in my previous reply as code) in the next post.

Also, regarding image insertion, I am using HTML and not direct insert image as it suits my usecase like "Photo": { "format": "html", "htmlText": "<html><body><img src='#{img_path}' width='25' height='25'></body></html>" }

Also, why I was trying to use both together was basically due to the fact that our templates are historical and use normal mail merge, so we don’t want to change those but we also want to insert images in a loop. So, the only way I knew was using mustache fields but since you have mentioned the post regarding regions(TableStart), I still have to try that. But now I am just curious about making both work together, so I have the knowledge about it.

Also, with TableStart, do we need to use with_regions: true? I would believe so.

@nikhilgoyal2205

Yes, please share the sample template along with the data.

Please find a sample template along with the data file. Hopefully, it will help you to insert an image in the template. HtmlImageTemplatemix.docx (14.7 KB)

HtmlImageTemplate.zip (12.3 KB)

Yes, you need to set with_regions to true for TableStart and TableEnd region as we do it for foreach block in mustache template.

Hi @tilal.ahmad

So, I have a simple template test_doc.docx (54.5 KB)
my data is

data = {
  "Date": "03/01/2010",
  "Photos": [
    {
      "Photo": { "format": "html", "htmlText": "<html><body><img src='#{img_path}' width='25' height='25'></body></html>" }
    },
    {
      "Photo": { "format": "html", "htmlText": "<html><body><img src='#{img_path}' width='25' height='25'></body></html>" }
    },
    {
      "Photo": { "format": "html", "htmlText": "<html><body><img src='#{img_path}' width='25' height='25'></body></html>" }
    }
  ]
}

img_path is a link to an image. You can use yours. For me, the template rendered the image 3 times as expected but it didn’t render the normal merge field Date.
Can you tell me the issue? The code used is

@words_api = AsposeWordsCloud::WordsApi.new
template = File.open('aspose_docs/test_doc.docx')
mail_merge_request = AsposeWordsCloud::ExecuteMailMergeOnlineRequest.new(template: template, data: data.to_json, with_regions: true)
output_doc = @words_api.execute_mail_merge_online(mail_merge_request)

@nikhilgoyal2205

Please note that you are populating region data using with_regions parameter true and the date is outside the region. So you are getting the issue.

In such a case, you have two options. Either you can run the mail merge method twice, once setting with_region to true and then false, or enclose the data in the top level region and update your template accordingly. Please check the following updated template and data file for reference.
test_doc.zip (51.0 KB)