1

I'm using JMustache, but I imagine this question would be the same for all implementations.

I'm using Mustache to Generate an XML file. When a list is empty, I don't want the parent tag to show. When the list is not empty, I want the parent tag to show once. I'm wondering what the Mustache Template should look like.

For example I might have either of the two XML files that need to be generated based on the data input:

<class>
    <name>Basketweaving</name>
    <students>
        <student>Joe Smith</student> 
        <student>Sally Smithers</student>
    </students>
</class>

or:

<class>
    <name>Basketweaving at a bad time</name>
</class>

The problem I'm having is if I define my template like this:

<class>
   <name>{{className}}</name>
   <students>
    {{#students}}
      <student>{{studentName}}</student>
    {{/students}}
   </students>
<class>

Then the empty class still has a students block.

e.g.

  <class>
        <name>Basketweaving at a bad time</name>
        <students>
</students>
    </class>

And if I move the loop:

<class>
   <name>{{className}}</name>
   {{#students}}
   <students>
      <student>{{studentName}}</student>
   </students>
   {{/students}}
<class>

I'll end up with Students repeated in the first example:

e.g.

<class>
    <name>Basketweaving</name>
    <students>
        <student>Joe Smith</student> 
    </students>
    <students>
        <student>Sally Smithers</student>
    </students>
</class>

So, what is the proper way to do the template to get my desired behavior?

Doug
  • 6,446
  • 9
  • 74
  • 107

3 Answers3

2

I figured out the answer to my own question. With JMustache the template should be something like the following:

<class>
   <name>{{className}}</name>
   {{#students}}
   {{#-first}}<students>{{/-first}}
      <student>{{studentName}}</student>
   {{#-last}}</students>{{/-last}}
   {{/students}}
<class>

The -first and -last are special flags that are only true the first or last iteration, respectfully, of the loop.

As a result the above code will ONLY output <students> while looping through the first entry in the students loop. Likewise </students> will only be output during the entry of the students loop.

This means that if students is an empty list, the <students> </students> will never be output, however if there is one or more entries the entries will be surrounded by <students> </students>.

Doug
  • 6,446
  • 9
  • 74
  • 107
1

I would suggest a two case approach:

  1. a null or empty list is simply omitted
  2. a non-empty list is rendered with its elements

How to render null or empty lists

Mustache's Inverted Section could render the "nothing found" case (e.g. an error-message or some alternative elements):

   {{^students}}
<!-- No students found --->
   {{/students}}

Would be rendered to:

<!-- No students found --->

But in your case you want nothing instead:

{{^students}}{{/students}}

How to render non-empty lists

Mustache's Non-Empty Lists would list all students for each with name wrapped inside the <students></students> collection.

But you only want the wrapping collection-element <students> to appear once for the list. Then you could instead test on the length or size property depending of your model being array or list:

{{#students.length}}
    <students>
    {{#students}}
        <student>{{studentName}}</student>
    {{/students}}
    </students>
{{/students.length}}

will result in this for example:

    <students>
        <student>Joe Smith</student> 
        <student>Sally Smithers</student>
    </students>

The length-conditional was inspired by the current top/accepted answer to similar question: How to handle string or array of strings in mustache template

Putting both cases together

So we add both template-parts inside the outer class root-element with always present child name:

<class>
   <name>{{className}}</name>
{{#students.length}}
    <students>
    {{#students}}
        <student>{{studentName}}</student>
    {{/students}}
    </students>
{{/students.length}}
{{^students}}{{/students}}
<class>

Benefits:

  • much cleaner and readable template
  • the 2 different cases are separated and communicated explicitly
hc_dev
  • 8,389
  • 1
  • 26
  • 38
0

Using the TemplateEncoder class from the HTTP-RPC project (I'm the author), you can do this:

<class>
   <name>{{className}}</name>
   {{?students}}
   <students>
      {{#.}}
      <student>{{studentName}}</student>
      {{/.}}
   </students>
   {{/students}}
<class>

If "students" exists and is non-empty, the content between the markers will be rendered. Otherwise, it will be ignored.

The "." character is a self-reference - within the "students" block, it refers to the students collection itself.

The "?" isn't "offical" Mustache syntax, but might be an option if JMustache is not a hard requirement.

Greg Brown
  • 3,168
  • 1
  • 27
  • 37
  • What a shift! Then (November 2020) recommending your tool, and recently you wrote about an excellent article about fast _Mustache_ for XML-templating: DZone (January 2021): [Transforming XML with Mustache Templates](https://dzone.com/articles/transforming-xml-with-mustache-templates) ️ – hc_dev Mar 18 '21 at 19:22
  • @hc_dev Can you elaborate on your comment? This suggestion is in response to the OP's question about generating XML using a template. The article you mentioned does the opposite - it transforms XML into another representation using a template. – Greg Brown Mar 19 '21 at 12:50
  • 1
    I perceived OP's question as very JMustache-centric (title, tag, body). So your answer suggesting another dependency seems to me very far from that. But I like you Mustache related article and wanted to encourage you to provide an answer using Mustache. Sorry if my comment was misleading – hc_dev Mar 22 '21 at 19:59
  • Thanks. I updated my answer to clarify the intent. – Greg Brown Mar 23 '21 at 17:30