1

I'm sending emails using the Java API TransmissionWithRecipientArray object against a template. I'm facing some problems with the substitution data. I have test this data in the template editor but I don't know how to introduce that substitution data using TransmissionWithRecipientArray.

Here is a sample:

(...), "offers": [
     {
       "description": "dddddddddddddddddd.",
       "discount": "ddddddd",
       "image": "ddddddddddddddddddddd",
       "image_announcer": "dddddddddddddddddddddddddddd",
       "alt_title": "dddddddddddddddddddddd",
       "tracking": "dhsdjkhsdjksdh",
       "name": "sdhsdohdsiosd",
       "id": "8480515",
       "announcer_paid": "0",
       "announcer_image": "test",
       "announcer_alt_title": "wdiohdiowdhiowd"
     },
      {
       "description": "dddddddddddddddddd.",
       "discount": "ddddddd",
       "image": "ddddddddddddddddddddd",
       "image_announcer": "dddddddddddddddddddddddddddd",
       "alt_title": "dddddddddddddddddddddd",
       "tracking": "dhsdjkhsdjksdh",
       "name": "sdhsdohdsiosd",
       "id": "8480515",
       "announcer_paid": "0",
       "announcer_image": "test",
       "announcer_alt_title": "wdiohdiowdhiowd"
     }, (...)

In other words the question is: What should we introduce in the method setSubstitutionData() to get this input as substitution data? We have validated the substitution data using the template editor.

transmission.setSubstitutionData(allSubstitutionData.asJava)

Mandatory HTML:

 {{offers[1].description}}
seldon851
  • 55
  • 9

3 Answers3

1

Per documentation, the way you loop through arrays in a template is:

{{ if offers }}
<ul>
  {{ each offer }}
  <li>Offer title is <b>{{ loop_var.name }}</b></li>
  {{ end }}
</ul>
{{ end }}

you need to use the variable loop_var and if you pass an object in the array, that loop_var will be the root of your object. So if you want to print your discount field, you would need to write loop_var.discount.

balexandre
  • 73,608
  • 45
  • 233
  • 342
  • Thanks for your answer @balexandre, but I need the data to be acceded like an Array, this way ==> {{offers[1].description}} – seldon851 Mar 15 '19 at 14:13
  • I need to know what I need to introduce in "setSubstitutionData" in order to have that data agains the template as an Array (like in the documentation [https://developers.sparkpost.com/api/template-language/#header-array-indexing]), not changing the HTML. – seldon851 Mar 15 '19 at 14:25
0

There are lots of samples for how to do that sort of thing here.

For your specific case, I think you want something like this.

      private void sendEmail(String from, String[] recipients) throws SparkPostException {
    TransmissionWithRecipientArray transmission = new TransmissionWithRecipientArray();

    // Populate Recipients
    List<RecipientAttributes> recipientArray = new ArrayList<RecipientAttributes>();
    for (String recipient : recipients) {
        RecipientAttributes recipientAttribs = new RecipientAttributes();
        recipientAttribs.setAddress(new AddressAttributes(recipient));
        recipientArray.add(recipientAttribs);
    }
    transmission.setRecipientArray(recipientArray);

    // Populate Substitution Data
    Map<String, Object> substitutionData = new HashMap<String, Object>();

    substitutionData.put("yourContent", "You can add substitution data too.");
    transmission.setSubstitutionData(substitutionData);

    // You can use Jackson, GSON or whatever you standard JSON decoding library is to
    // Build this structure.
    List<Map<String, String>> offers = new ArrayList<Map<String, String>>();
    for (int i = 0; i < 2; i++) {

        Map<String, String> offer = new HashMap<String, String>();
        offer.put("description", "description value " + i);
        offer.put("discount", "discount " + i);
        offer.put("image", "image " + i);
        offer.put("image_announcer", "image_announcer " + i);
        offer.put("alt_title", "alt_title " + i);
        offer.put("tracking", "tracking " + i);
        offer.put("name", "name " + i);
        offer.put("id", "id " + i);
        offer.put("announcer_paid", "announcer_paid " + i);
        offer.put("announcer_image", "announcer_image " + i);
        offer.put("announcer_alt_title", "announcer_alt_title " + i);

        offers.add(offer);
    }

    substitutionData.put("offers", offers);

    // Populate Email Body
    TemplateContentAttributes contentAttributes = new TemplateContentAttributes();
    contentAttributes.setFrom(new AddressAttributes(from));
    contentAttributes.setSubject("☰ Your subject content here. {{yourContent}}");
    contentAttributes.setText("You could do it for text too. See https://www.sparkpost.com/blog/advanced-email-templates/ for an example");
    contentAttributes.setHtml(
            "<b>Your Data:</b><br>\n"
                    + "<table border='1'>\n"
                    + "    <tr>\n"
                    + "        <th>description</th>\n"
                    + "        <th>discount</th>\n"
                    + "        <th>image</th>\n"
                    + "        <th>image_announcer</th>\n"
                    + "        <th>alt_title</th>\n"
                    + "        <th>tracking</th>\n"
                    + "        <th>name</th>\n"
                    + "        <th>id</th>\n"
                    + "        <th>announcer_paid</th>\n"
                    + "        <th>announcer_image</th>\n"
                    + "        <th>announcer_alt_title</th>\n"
                    + "    </tr>\n"
                    + "    {{each offers}}    \n"
                    + "        <tr>\n"
                    + "            <td> {{{offers.description}}} </td>\n"
                    + "            <td> {{{offers.discount}}} </td>\n"
                    + "            <td> {{{offers.image}}} </td>\n"
                    + "            <td> {{{offers.image_announcer}}} </td>\n"
                    + "            <td> {{{offers.alt_title}}} </td>\n"
                    + "            <td> {{{offers.tracking}}} </td>\n"
                    + "            <td> {{{offers.name}}} </td>\n"
                    + "            <td> {{{offers.id}}} </td>\n"
                    + "            <td> {{{offers.announcer_paid}}} </td>\n"
                    + "            <td> {{{offers.announcer_image}}} </td>\n"
                    + "            <td> {{{offers.announcer_alt_title}}} </td>\n"
                    + "        </tr>\n"
                    + "    {{ end }} \n"
                    + "</table>\n\n");
    transmission.setContentAttributes(contentAttributes);

    transmission.setContentAttributes(contentAttributes);

    // Send the Email
    IRestConnection connection = new RestConnection(this.client, getEndPoint());
    Response response = ResourceTransmissions.create(connection, 0, transmission);

    logger.debug("Transmission Response: " + response);

This is what the result looks like:

enter image description here

Yepher
  • 1,465
  • 12
  • 25
  • Thanks for your answer @Yepher. Is it possible to put a Json object ? In that case, which library would be better? – seldon851 Mar 18 '19 at 09:31
  • Yes, you can use JSON no problem. Use GSON or Jackson library they will produce a Map or List depending on the JSON you give it. I just did it this way as to not have a dependence on an external library. If your not clear on how to do that StackOverflow has many answers for how to parse JSON in Java. – Yepher Mar 18 '19 at 21:24
0

Thank you guys for your answers.

The issue we had here were with the conversion from a Scala Map type to Gson.

The result of processing with the Gson library HashMaps created from Scala Maps is different. Includes extra fields and changes the structure of the JSON.

The solution is this answer for Java users, and for Scala: iterate firstly all Maps converting to Java types like this:

 def toJavaConverter(objectLevelSubs: immutable.Map[String, AnyRef]): java.util.LinkedHashMap[String, Object] = {
val output = new java.util.LinkedHashMap[java.lang.String, Object]
objectLevelSubs.foreach {      
  case (k: String, v: List[Predef.Map[String, AnyRef]]) => output.put(k, v.map(toJavaConverter))     
  case (k: String, v: Predef.Map[String, AnyRef]) => output.put(k, toJavaConverter(v))
  case (k: String, v: AnyRef) => output.put(k, v)
}
output}

And finally converting each element like this.

 val gson: Gson = new GsonBuilder().setPrettyPrinting().enableComplexMapKeySerialization().create()
val finalSubstitutionData: util.LinkedHashMap[String, AnyRef] = new util.LinkedHashMap[String, AnyRef]()

javaObjectLevelSubs.forEach{
  case (k: String, v: String) => finalSubstitutionData.put(k, v)
  case (k: String, a) => a match {case l: List[_] => finalSubstitutionData.put(k, l.map(gson.toJsonTree).asJava)}
}

Thanks @Yepher and @balexandre

seldon851
  • 55
  • 9