Custom JSON feeds are valuable in many situations, and while it is easy to create a JSON feed in Marketpath CMS there are a few challenges that you should be aware of in order to prevent frustration.
There are numerous ways to collect and prepare your data - exactly the same as for a normal page load. The exact method you use will depend on what you are trying to accomplish, but here are two suggestions that work well and are easy both to create and maintain. Note that you can easily combine the two for further power and flexibility:
In your JSON template, define custom fields for your content editors to fill out. This is the data that will be output in the JSON feed. Fieldsets are particularly useful for allowing editors to fill out repeating data structures (JSON arrays).
There are dozens of ways that you can pull dynamic content from Marketpath CMS in your template - each of which can be combined with the others. A few examples are:
One of the beautiful things about making JSON templates is that, if you build them with enough flexibility, you can re-use the same templates and/or pages multiple times for similar purposes by providing different input either through the custom fields on the page or through query string parameters passed in the request (or both).
Before beginning to output your data in the template (well, order doesn’t actually matter too much here, but it is generally more readable to put this at the top of your template), there are a few methods that you need to call so that your scripts can access and utilize your data:
{% allow_cors always %}
, otherwise you will need to test that the origin matches the domain that you want to allow (eg: {% if request.headers.origin == 'https://specifically-allowed-domain.com' %}{% allow_cors %}{% endif %}
).Outputting JSON to the template is exactly the same as outputting HTML or any other content types. There is one special challenge, however, due to the nature of the liquid syntax and how it conflicts with JSON syntax - both languages rely heavily on the “{“ and “}” characters:
Outputting a single JSON property to the template could not be simpler with the “json_encode” filter. When using this filter you do not need to concern yourself with whether or not the value is null or an empty string.
"custom_value":{{ entity.custom_value | json_encode }}
Outputting a JSON object, on the other hand, can be a little more tricky due to the bracket limitation. The most simple workaround for this limitation is to output JSON brackets as a variable - particularly when it would otherwise result in multiple brackets next to each other in the liquid template. The "url_decode" filter makes defining the variable easy:
{"url":{{entity.full_url | json_encode}}}
{"url":{{entity.full_url | json_encode}} }
{"url":{{entity.full_url | json_encode}}{{ '%7D' | url_decode }}
{% var openbracket = '%7B' | url_decode %}{% var closebracket = '%7D' | url_decode %}{{ openbracket }}"url":{{entity.full_url | json_encode}}{{ closebracket }}
Here is an example of a very simple template which will output the next 10 blog posts with a post date in the future (note: if this is intended to be usable outside of your own website you will need to add {% allow_cors always %} to the template.
{% set_content_type 'application/json' -%}
{%- var openbracket = '%7B' | url_decode -%}
{%- var closebracket = '%7D' | url_decode -%}
{%- blog_posts posts = start_date:request.date limit:10 sort_by:'post_date' sort_direction:'asc' -%}
[{% for post in posts %}{{openbracket}}
'title':{{post.title.value | json_encode}}
,'post_date':'{{post.post_date | format: 'o' }}'
,'summary':{{post.summary_html.value | json_encode}}
,'blog':{{post.blog.title.value | json_encode}}{% if post.image.is_valid %}
,'image':{{post.image.path | json_encode}}{% endif %}
{{closebracket}}{% unless forloop.last %},{% endunless %}{% endfor %}]
For a fully-featured JSON template example, install the Flexible Blog Post JSON Feed package on your site. This package contains a single template which outputs a blog feed. The JSON structure is configurable at the page level and the blog posts returned are configurable via query parameters. Pagination-related information is returned using X-Pagination-* headers.