Doug
Doug

Reputation: 7077

Using Mustache Template with XML

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?

Upvotes: 1

Views: 3846

Answers (3)

Greg Brown
Greg Brown

Reputation: 3254

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.

Upvotes: 0

hc_dev
hc_dev

Reputation: 9377

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

Upvotes: 1

Doug
Doug

Reputation: 7077

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

Upvotes: 2

Related Questions