6

I'm trying to generate a JSON file with mustache with the following template:

{
    "name": "{{customer_info.first_name}}",
    "email": "{{contact_info.email}}",
    "campaign": {
        "campaignId": "{{contact_info.campaign.campaignId}}"
    },
    "tags": [
        {{#contact_info.tags}} 
        {
            "tagId": "{{tagId}}"
        },
        {{/contact_info.tags}}
    ]
}

As an output example I get:

{
    "name": "Antonio",
    "email": "myemail@gmail.com",
    "campaign": {
        "campaignId": "pfft"
    },
    "tags": [
        {
            "tagId": "6prrtAP"
        },
        {
            "tagId": "64rrrE9"
        },
    ]
}

Which unluckily is a BAD FORMATTED JSON, because there is a not wanted "," after the last element in the array.

Can any of you help me in solving this issue and remove the comma ?

Thanks a lot

AntLomb
  • 69
  • 1
  • 1
  • 2

5 Answers5

2

I would do this:

var md = {};
var tagsCount = 2;
var currTagIndex = 0;
md['show_comma'] = function(){
    currTagIndex++;
    return currTagIndex <= tagsCount;
}

Then in Mustache template:

{{#show_comma}}
,
{{/show_comma}}
MohamedSanaulla
  • 6,112
  • 5
  • 28
  • 45
  • This solution assumes JavaScript as the host language and it might not be portable to every other host language. Still an elegant solution, though! – Julian Feb 27 '23 at 19:03
2

Try using SelectTransform npm package. It has Mustache like syntax without all the side-effects that Mustache creates and the package size is also not as heavy as Handlebars.js

import ST from "stjs";
 
const data = {
  name: 'Jakub',
  friends: [
    {
      name: 'Michal'
    }
  ]
};
 
const template = {
  newName: '{{ name }}',
  friends: {
    '{{ #each friends }}': {
      subName: '{{ name }}'
    }
  }
};
 
console.log(ST.select(data).transformWith(template).root());
 
// Result:
/**
 * {
 *   "newName": "Jakub",
 *   "friends": [
 *     {
 *       "subName": "Michal"
 *     }
 *   ]
 * }
 */
theSereneRebel
  • 196
  • 3
  • 16
1

Don't generate JSON from textual templates. You'll constantly face problems like this. Superfluous commas, meta characters in strings (what if customer_info.first_name contains double quotes), failing to properly nest structures etc.

Generate your data as native structures in your programming language, and encode it as JSON using library provided by your programming language.


However, if you absolutely need, try to generate as much JSON data as possible (ideally, self-contained JSON fragment) outside template, and interpolate that inside template. For example:

let contact_info = {"tags": [ "6prrtAP", "64rrrE9" ]}
let tags = contact_info.tags.map((tag) => ({"tagId": tag})); // [{tagId: "6prrtAP"}, {tagId: "64rrrE9"}]
let tagsJSON = JSON.stringify(tags); // "[{\"tagId\":\"6prrtAP\"},{\"tagId\":\"64rrrE9\"}]"

Then, pass tagsJSON to your template:

{
    "name": "{{customer_info.first_name}}",
    "email": "{{contact_info.email}}",
    "campaign": {
        "campaignId": "{{contact_info.campaign.campaignId}}"
    },
    "tags": {{tagsJSON}}
}

That way, tagsJSON always contains valid JSON-encoded data, so it might be safely interpolated as a value in JSON dictionary/object. Even if tag list is empty, even if tag IDs suddenly start to contain characters that need escaping etc. All corner cases are already handled for you.

  • 2
    He didn't ask for a lecture. Sometimes it makes sense to use a template if you know only a very small, controllable portion of the code is going to change. Or you've inherited it from someone else and can't re-engineer it. You don't even attempt an actual solution, I'm not sure why this got upvoted at all. – Scott Byers Feb 07 '22 at 19:27
  • 2
    Thanks, that makes sense. I've edited my answer to include an actual solution. – el.pescado - нет войне Mar 07 '22 at 22:48
1

I've been experiencing some similar problem and I found out that Handlebars is a lot similar to mustache and way more powerful.

You could check that out and try using this template to solve your problem, without adding anything to your current model.

{
    "name": "{{customer_info.first_name}}",
    "email": "{{contact_info.email}}",
    "campaign": {
        "campaignId": "{{contact_info.campaign.campaignId}}"
    },
    "tags": [
        {{#each contact_info.tags}} 
        {
            "tagId": "{{tagId}}"
        }{{#unless @last}},{{/unless}}
        {{/each}}
    ]
}
oniramarf
  • 843
  • 1
  • 11
  • 27
0

This looks like a good answer:

contact_info['tags'][ contact_info['tags'].length - 1 ].last = true;

and the template would be

{{#contact_info.tags}} 
{
   "tagId": "{{tagId}}"
} {{^last}}, {{/last}}
{{/contact_info.tags}}

Source: https://stackoverflow.com/a/7591866

gmansour
  • 889
  • 8
  • 8