Correct syntax for addressing JSON object in Jekyll _data file - json

I am creating a page in Jekyll and attempting to use data in a JSON file stored in Jekyll's "_data" folder. The JSON file is "/_data/objectsandproperties.json" and contains:
{
"objectA": { "propertyA": "FooA", "propertyB": "BarA" },
"objectB": { "propertyA": "FooB", "propertyB": "BarB" },
"objectC": { "propertyA": "FooC", "propertyB": "BarC" }
}
I would like to output a list formatted like this:
<dl>
<dt>objectA</dt>
<dd>propertyA: FooA</dd>
<dd>propertyB: BarA</dd>
<dt>objectB</dt>
<dd>propertyA: FooB</dd>
<dd>propertyB: BarB</dd>
<dt>objectC</dt>
<dd>propertyA: FooC</dd>
<dd>propertyB: BarC</dd>
</dl>
I am currently using Liquid tags in my markdown file like this:
{% for objects in site.data.objectsandproperties %}
<dl>
{% for object in objects %}
<dt>Object names: {{ object }}</dt>
<dd>propertyA: {{ object.propertyA }}</dd>
<dd>propertyB: {{ object.propertyB }}</dd>
{% endfor %}
</dl>
{% endfor %}
This is not working as the object is not "objectA" but the entire objectA object with properties etc.
I don't have access to the script that creates the JSON file so I cannot add a label or make it an array etc. I'm hoping I can get this to work with Liquid.
Thanks.

Using the inspect filter to better understand what we are working with :
{% for object in site.data.objectsandproperties %}
{{ object | inspect }}
>> returns an array like :
>> ["objectA", {"propertyA"=>"FooA", "propertyB"=>"BarA"}]
name : {{ object.first }} or {{ object[0] }}
properties : {{ object.last}} or {{ object[1] }}
{% endfor %}
Your code can look like :
<dl>
{% for object in site.data.objectsandproperties %}
<dt>Object names: {{ object | first }}</dt>
<dd>propertyA: {{ object.last.propertyA }}</dd>
<dd>propertyB: {{ object.last.propertyB }}</dd>
{% endfor %}
</dl>

You can access and print keys of data by utilizing the attributes of the forloop object:
<dl>
{%- for object in site.data.objectsandproperties %}
{%- for prop in object %}
{%- if forloop.first %}
<dt>{{ prop }}</dt>
{%- else %}
{%- for subprop in prop %}
<dd>{{ subprop[0] }}: {{ subprop[1] }}</dd>
{%- endfor %}
{%- endif %}
{%- endfor %}
{%- endfor %}
</dl>

Related

Jinja Undefined error when using methods on float

I have a dictionary object. I can iterate through it using:
{% for key,val in my_dict.items() %}
String: {{ my_dict[key]['my_string'] }}<br>
Float: {{ val['my_float'] }}
{% endfor %}
This will give me something like:
String: I'm a string
Float: 0.72000001
If I try to use a method like:
{% for key,val in my_dict.items() %}
String: {{ my_dict[key]['my_string'] }}<br>
Float: {{ val['my_float']|round(2) }}
{% endfor %}
or
{% for key,val in my_dict.items() %}
String: {{ my_dict[key]['my_string'] }}<br>
Float: {{ "%.2f"|format(val['my_float']) }}
{% endfor %}
I get jinja2.exceptions.UndefinedError: 'dict object' has no attribute 'my_float'
I get a similar error when I call val['my_list'][0], even though I can see it is a list when I just use {{ val['my_list'] }} as it give me something like:
[4.91, 10.16, 19.53, 21.09, 34.27, 52.11, 9.93]
This only seems to be happening with floats and I have no idea why.
I am using jinja2 v3.0.3.

Listing all the key-value properites in site.pages

On my blog, I have a debug page which lists several things, including all the properties for each page in site.pages, like so:
{%- for page in site.pages -%}
<strong>{{ page.path }}:</strong><br>
<table>
{% for key_value in page %}
{% if key_value[0] == "content" %}
<tr><td>content</td><td>[{{ key_value[1] | size }} characters]</td></tr>
{% else %}
<tr><td>{{ key_value[0] }}</td><td><span>{{ key_value[1] }}</span></td></tr>
{% endif %}
{% endfor %}
</table>
{%- endfor -%}
This produces output like this:
So far, so good.
Now, I want to do the exact same thing with site.posts, but it doesn't work.
In particular, key_value[0] and key_value[1] don't have any value. The iteration does produce something, but it is not a key an value.
For example, the following produces a list of key names:
{%- for pp in site.posts -%}
<strong>{{ pp.path }}:</strong><br>
{% for kv in pp %}
{% capture cap %}{{ kv }}{% endcapture %}
<div>{{ cap }}</div>
{% endfor %}
<br>
{%- endfor -%}
like so:
I can't seem to get the content of the properties in a generic way, however. I guess the type of the things in the site.posts is not the same as sites.pages, in particular not a simple liquid dictionary.
I've tried messing around with to_liquid mentioned here without luck.

One variable for different collections in Jekyll to use in forloop

I have several collections on my Jekyll site. I've added post navigation to one of the collections displaying a counter on each post page:
{% assign testimonials = site.testimonials %}
{% assign page_order = 1 %}
{% for node in testimonials reversed %}
{% if node.url == page.url %}
{{ page_order }} from {{ forloop.length }}
{% else %}
{% assign page_order = page_order | plus: 1 %}
{% endif %}
{% endfor %}
I would like to make this code work not only for site.testimonials, but for other collections as well. I tried to pass a variable for collections like this:
{% capture label %}{{ page.collection }}{% endcapture %}
{% assign collection = site.collections | where: "label",label | first %}
{% for node in collection reversed %}
{% if node.url == page.url %}
{{ page_order }} from {{ forloop.length }}
{% else %}
{% assign page_order = page_order | plus: 1 %}
{% endif %}
{% endfor %}
But it doesn't work. Is there any way to pass a variable for all collections in Jekyll to use in forloop in post navigation?
When you access collection with site.testimonials, you get collection's documents array.
{{ site.testimonials | inspect }}
# output >> [#<Jekyll::Document ...>, #<Jekyll::Document ...>, ...]
When you access a collection while looping over site.collection, you receive the collection's object :
{% assign collection = site.collections | where: "label",page.collection | first %}
{{ collection | inspect }}
# output >> { "output": true, "label": "collectionLabel",
"docs": [ doc1, docs2, ... ], "files": [],
"directory": "/path/to/collection",
"relative_directory": "_colectionDirectory" }
In your case, you just have to replace :
{% for node in collection reversed %}
By :
{% for node in collection.docs reversed %}

Liquid + Jekyll, "unless" not working inside "for" loop

I'm making JSON given a set of pages. I want to skip any pages without titles, and the last element can not have a comma after it, that's bad JSON. Tried different variations, here is an example:
---
---
[
{% for page in site.pages %}
{% unless page.title %}
{% continue %}
{% else %}
{
"title":"{{ page.title }}",
"content":"{{ page.content | strip_html | strip_newlines }}",
"href":"{{ page.url }}"
}
{% endunless %}
{% unless forloop.last %},{% endunless %}
{% endfor %}
]
The end of the resulting JSON file looks like this:
{
"title":"Test Search",
"content":" ",
"href":"/search.html"
}
,
]
How do I get rid of the trailing comma? Thank you in advance.
I think your problem is that your last loop iteration is one that has no title.
Try to prepend the comma. That way you don't have to look in the future:
{% assign isFirst = true %}
{% for page in site.pages %}
{% unless page.title %}{% continue %}{% endunless %}
{% unless isFirst %},{% endunless %}
{% assign isFirst = false %}
{
"title": {{ page.title | jsonify }},
"content": {{ page.content | strip_html | strip_newlines | jsonify }},
"href": {{ page.url | jsonify }}
}
{% endfor %}
Edit: You should also use the jsonify filter to ensure proper escaping of quotes and other characters.
Try using {% raw %} to escape your non-liquid brackets, this can cause Jekyll to misbehave without throwing any errors.
If that still leads to odd behavior try creating a seperate array like the following example, which is very similar to the case from OP.
{% assign suggestions = "" | split: ',' %}
{%- for item in site[page.collection] -%}
{%- unless item.url == page.url -%}
{% assign suggestions = suggestions | push: item %}
{%- endunless -%}
{%- endfor -%}
<script id="suggest-json" type="application/json">
[
{%- for item in suggestions -%}
{% assign images = item.media | where: "type", "image" %}
{% assign image = images.first %}
{% raw %}
{
{% endraw %}
"url": "{{ site.baseurl }}{{ item.url }}",
"title": {{ item.title | jsonify }},
"subject": {{ item.game | jsonify }},
"image": {{ image.thumbnail | jsonify }},
"alt": {{ image.thumbnail_alt | jsonify }}
{% raw %}
}
{% endraw %}
{%- unless forloop.last -%},{%- endunless -%}
{%- endfor -%}
]
</script>
The problem may be that Jekyll doesn't actually consider the forloop.index as last because it isn't technically. You can avoid this by creating a new array either with a loop or the 'where' tag (loop used above).
The forloop.last should now behave properly, hope this helps.

How to show code with octopress

I want to display on my site built with Octopress the following text:
<ul>
{% for item in site.categories %}
<li>{{ item[0] | capitalize }} [ {{ item[1].size }} ]</li>
{% endfor %}
</ul>
I inserted { % raw %} before and respectively {% endraw ½} after my text.
{% raw %}
<ul>
{% for item in site.categories %}
<li>{{ item[0] | capitalize }} [ {{ item[1].size }} ]</li>
{% endfor %}
</ul>
{% endraw ½}
The result is:
{% for item in site.categories %}
List item
{{ item[0] | capitalize }} [ {{ item[1].size }} ]
{% endfor %}
I was expected:
<ul>
{% for item in site.categories %}
<li>{{ item[0] | capitalize }} [ {{ item[1].size }} ]</li>
{% endfor %}
</ul>
There are two approaches your can take, I am assuming you are trying to put this content in a markdown file and use the kramdown markdown processor in Jekyll.
Using the 'Raw' tag and codeblocks
{% raw %}
~~~
<ul>
{% for item in site.categories %}
<li>{{ item[0] | capitalize }} [ {{ item[1].size }} ]</li>
{% endfor %}
</ul>
~~~
{% endraw %}
The raw tag needs to be used to prevent the liquid tags from being processed, and the fenced code block prevents the HTML from being passed straight through the markdown processor. It will be presented as a code block as above.
Using 'Raw' and 'Highlight'
If you want the HTML to be highlighted you can use this approach. Unfortunately Pygments does not do highlighting of liquid tags.
{% highlight html %}
{% raw %}
<ul>
{% for item in site.categories %}
<li>{{ item[0] | capitalize }} [ {{ item[1].size }} ]</li>
{% endfor %}
</ul>
{% endraw %}
{% endhighlight %}