AntLomb
AntLomb

Reputation: 69

How to render a JSON template using mustache

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": "[email protected]",
    "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

Upvotes: 6

Views: 16492

Answers (5)

gmansour
gmansour

Reputation: 1000

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

Upvotes: 0

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.

Upvotes: 2

theSereneRebel
theSereneRebel

Reputation: 306

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"
 *     }
 *   ]
 * }
 */

Upvotes: 2

oniramarf
oniramarf

Reputation: 913

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}}
    ]
}

Upvotes: 1

MohamedSanaulla
MohamedSanaulla

Reputation: 6252

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}}

Upvotes: 4

Related Questions