Hugo Tips and Tricks #1

Transforming Dynamic Data via Dispatch Strategy and Set Logic

By on ::      

I’ve been exploring options for dynamic rendering in Hugo.

It took a bit to stumble upon how to render an enumerable (e.g. an Array or Hash) as valid JSON . The primary problem is how to handle the commas AND apply any transformations on the Array/Slice or Hash/Dictionary.

In Hugo, if I have a Slice {{- $slice := slice("title" "created_at" "author") }}, I can use the delimit function to render that slice. For example {{ delimit $slice "; " }} would render title; created_at; author. However I need to do something different if I want to use the $slice as a key to lookup data and render that data according to its attributes.

Original JSON Document

Lets say I have a JSON document:

{
  "title": {
    "type": "html",
    "label": "Title",
    "value": "<h1>Hugo Demo<h1><h2>Creative Approaches to Iteration</h2>"
  },
  "author": {
    "type": "text",
    "label": "Author",
    "value": "Jeremy Friesen"
  },
  "created_at": {
    "type": "date",
    "label": "Date",
    "value": "2020-05-01"
  }
}

Notice the trailing comma at the end of the “title” and “author” line. There is no comma at the end of the “created_at” line.

Transformed JSON Document

I want to transform the original JSON into the following Transformed JSON:

{
  "Title": "\u003ch1\u003Hugo Demo\u003c/h1\u003\u003ch2\u003Creative Approaches to Iteration\u003c/h2\u003",
  "Author": "Jeremy Friesen",
  "Date": "May 1, 2020"
}

In the transformed JSON, I’ve taken the original JSON document, and transformed it. The title attribute is JSON encoded HTML. I’ve chosen to render the date attribute in a “natural language format”.

Let’s walk through the steps. I added a ./data/attributes.json document. This conforms to Hugo’s Data Templates.

Partials

I then wrote three partials, all in the ./layouts/partials/default directory. Note: these partials map to the type attribute in the original JSON. The .label and .value references in the below templates are form the attributes of the original JSON.

date.json

"{{ .label }}": "{{ dateFormat "Jan 2, 2006" .value }}"

Note, I’m using Hugo’s dateFormat function to transform from an ISO8601 format into a “human readable” format. Normally, I would never transport JSON with a date in this “human readable” format, but this is part of the exercise.

html.json

"{{ .label }}": {{ .value | jsonify }}

The jsonify ensures that we have a compliant JSON element. It also renders the enclosing paranthesis.

text.json

"{{ .label }}": {{ .value | jsonify }}

Again, using jsonify to ensure we have compliant JSON.

Stitching it All Together

I then use the following Hugo template to render the JSON.

{{/* Here we need to gather all the keys for the attributes. We need just the
     keys, as they are strings, and can later be compared when we call the
     `complement` function. In hugo, we cannot run the `compliment` on a
     map/dict object
*/}}
{{- $keys := slice }}
{{- range $key, $data := .Site.Data.attributes }}
  {{- $keys = $keys | append $key }}
{{- end }}
{{- $last_key := $keys | last 1 }}

{{/* NOTE: The next line is the open bracket for the resulting JSON document */}}
{
{{- range $key := ($keys | complement $last_key) }}
  {{- $predicate := index $.Site.Data.attributes $key }}
  {{- $partialName := (printf "default/%s.json" $predicate.type )}}
  {{ partial $partialName $predicate }},
{{- end }}
{{/* NOTE: The above range and below range are almost exact copies. However,
     in the above, after we render the partial, we add a comma. In the below
     range, we do not add a comma after we render the partial */}}
{{- range $key := $last_key }}
  {{- $predicate := index $.Site.Data.attributes $key }}
  {{- $partialName := (printf "default/%s.json" $predicate.type )}}
  {{ partial $partialName $predicate }}
{{- end }}
{{/* NOTE:  The next line is the close bracket for the resulting JSON document */}}
}

Conclusion

With the above Hugo template and partials, I transformed the original JSON document into a new JSON document.

I hope this provides some insight in how you might use dynamic data, render dispatching strategies, and some set logic to transform the dynamic data.

If you’d like a working example, please take a look at github.com/jeremyf/hugo-dynamic-data