Escaping a JSON string

Hi,

I’m building a JSON Page. Basically, I want to list all the posts in my blog using a JSON format:

---
---
[
    {% for post in site.posts %}
        {
            "url" : "{{ post.url }}",
            "title" : "{{ post.title | escape }}",
            "published" : "{{ post.date }}",
            "content" : "{{ post.content | escape }}",
            "excerpt" : "{{ post.excerpt | escape }}",
            "categories" : {{ post.categories | sort  | prepend: "1" | remove_first:  "1" }},
            "tags" : {{ post.tags | sort | prepend: "1" | remove_first:  "1" }}
        } {% unless forloop.last %},{% endunless %}
    {% endfor %}
]

I do not know how to escape the strings using a “JSON String escape”. The only escape / url_encode available (cf https://shopify.github.io/liquid/) do not implement JSON escaping.

Is there a simple way to do that?

Cheers,

Vincent-Philippe

there is a jsonify filter - https://jekyllrb.com/docs/liquid/filters/

maybe {{ site.posts | jsonify }} would do the whole thing for you? not sure. You’re worried about one of the things containing a { or something and that needs to be escaped right? I have seen examples of people who just do it like you have without escaping, as well as people who make their own plugin to do it properly, but not sure I have seen something like you have that would actually work properly if the data contained stuff that could break it.

Hi Rdyar,

Thank you for your quick response. I didn’t know about the Jekyll-only liquid filters.

It does work! I’ve tried the following:

[
    {% for post in site.posts %}
        {{ post | jsonify }} {% unless forloop.last %},{% endunless %}
    {% endfor %}
]

and it does spit out all the post in a proper JSON format.

That ends up using quite a lot of bandwidth though as the post object (hash?) has 20 elements where I just need 4-5 of them.

Is there a way to prune out the post object? I haven’t seen anything like that and it seems to be impossible to create an hash out of nothing…

The other solution could be to map one property at a time and reassemble it on the search-job side…

Thank you again Rdyar!

I don’t know of any way to exclude stuff, not saying it can’t be done but it is not anything I know how to do.

FYI & for the thread, what I ended up doing is:

---
---
{
    "url" : {{ site.posts | map:  "url" | jsonify }},
    "title" : {{ site.posts | map:  "title" | jsonify }},
    "published" : {{ site.posts | map:  "published" | jsonify }},
    "content" : {{ site.posts | map:  "content" | jsonify }},
    "excerpt" : {{ site.posts | map:  "excerpt" | jsonify }},
    "categories" : {{ site.posts | map:  "categories" | jsonify }},
    "tags" : {{ site.posts | map:  "tags" | jsonify }}
}

So basically, I output a JSON document with 7 sub-arrays of the same length. It’s not super elegant but it does work as I don’t have gazillion posts and can load the entire JSON document in memory in C# and zip the arrays together for Azure Search processing. It works with the limitation of Liquid.

And it works!

Thank you!

Vincent-Philippe

Just to follow up on your idea, a singlet variable (a string, an integer, a number, true/false) can be properly escaped to JSON format with jsonify – double quotes included for strings.

For context, I was trying to add JSON feed to my blog, and each item ended up like:

[{% for post in site.posts %}
  {
    "title": {{post.title | jsonify}}, 
    "content_html": {{post.content | jsonify}}
  },
{% endfor %}]

Note that I did not type the surrounding quotes; jsonify does it for me. Also the trailing comma in the list is not handled – you might want to do something about that :slight_smile: