DEV Community

Cover image for TIDBIT: formatting JSON in a templating language
Charles Loder
Charles Loder

Posted on

TIDBIT: formatting JSON in a templating language

Formatting JSON in a templating language can be difficult if there are optional fields. In particular, trailing commas can cause invalid JSON, which happens often if there are optionally defined fields.

This article shows how to make it safer to template out JSON.

The examples will be using Liquid to template out a LD+JSON snippet of a schema.org Book.

TLDR: use a leading comma instead of a typical trailing comma

{% if book.numberOfPages %}
  , "numberOfPages": {{ book.numberOfPages }}
{% endif %}
Enter fullscreen mode Exit fullscreen mode

Basic example

This example is pretty simple:

{
  "@context": "http://schema.org",
  "@type": "Book",
  "name": "{{ book.name }}",
  "description": "{{ book.description }}"
}
Enter fullscreen mode Exit fullscreen mode

The name and description are from our Liquid code. Even if there is no name and description, the JSON will render correctly.

More fields

Because our product is a Book, we can add fields specific to a book, like bookFormat, isbn, and numberOfPages.

{
  "@context": "http://schema.org",
  "@type": "Book",
  "name": "{{ book.name }}",
  "description": "{{ book.description }}",
  "bookFormat": "Hardback",
  "isbn": "{{ book.isbn }}",
  "numberOfPages": {{ book.numberOfPages }}
}
Enter fullscreen mode Exit fullscreen mode

Optional fields

What if the bookFormat is an EBook instead of a Hardback, what do we do with the numberOfPages?

Solution 1: default

We could use a default value:

{
  "@context": "http://schema.org",
  "@type": "Book",
  "name": "{{ book.name }}",
  "description": "{{ book.description }}",
  "bookFormat": {{ book.bookFormat }},
  "isbn": "{{ book.isbn }}",
  "numberOfPages": {% if book.bookFormat == "EBook" %} 0 {% else %} {{ book.numberOfPages }} {% endif %}
}
Enter fullscreen mode Exit fullscreen mode

That works, but it is semantically awkward to have a numberOfPages property on a electronic item.

Solution 2: conditionally add the field

Instead of a default value, we can conditionally add the field:

{
  "@context": "http://schema.org",
  "@type": "Book",
  "name": "{{ book.name }}",
  "description": "{{ book.description }}",
  "bookFormat": {{ book.bookFormat }},
  "isbn": "{{ book.isbn }}",
  {% if book.numberOfPages != blank %}
    "numberOfPages": {{ book.numberOfPages }}
  {% endif %}
}
Enter fullscreen mode Exit fullscreen mode

That makes a lot more sense.

But wait! There's one issue. Do you see it?

If there is no numberOfPages, then we have invalid JSON because of the trailing comma!

Solution 3: a leading comma

In order to avoid issues with a trailing comma, we can use leading commas:

{
  "@context": "http://schema.org",
  "@type": "Book",
  "name": "{{ book.name }}",
  "description": "{{ book.description }}",
  "bookFormat": {{ book.bookFormat }},
  "isbn": "{{ book.isbn }}"
  {% if book.numberOfPages != blank %}
    ,"numberOfPages": {{ book.numberOfPages }}
  {% endif %}
}
Enter fullscreen mode Exit fullscreen mode

Regardless of whether or not the numberOfPages field exists, the JSON will always be valid!

Even adding as many fields as we need, it will always be valid:

{
  "@context": "http://schema.org",
  "@type": "Book",
  "name": "{{ book.name }}",
  "description": "{{ book.description }}",
  "bookFormat": {{ book.bookFormat }},
  "isbn": "{{ book.isbn }}"
  {% if book.numberOfPages != blank %}
    ,"numberOfPages": {{ book.numberOfPages }}
  {% endif %}
  {% if book.illustrator != blank %}
    ,"illustrator": {{ book.illustrator }}
  {% endif %}
  {% if book.abridged != blank %}
    ,"abridged": {{ book.abridged }}
  {% endif %}
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

It's a pretty simple fix that can save you quite a lot of time debugging invalid JSON.

It helped me, I hope it helps you!

Top comments (0)