Edit on 3/26/2018 to fix tags, thanks to commenter Carl Recently a new specification was launched to recreate RSS in JSON, JSON Feed. For folks who may not be aware, RSS is an XML spec (well, multiple ones) for sharing content between sites. Blogs, primarily, and content-heavy sites typically make use of this. I'm not sure how many people outside of developers actually use RSS, but it's still definitely a "thing" even if you don't necessarily think of it when thinking about APIs.
The idea behind JSON Feed is to simply recreate the same, or similar, functionality in JSON as opposed to XML. You can read more about the launch on their announcement post and read the full spec as well. (And actually, this is one of the better written specs I've seen. You won't need a PhD in CompSci to grok it.)
I thought it might be fun to build a JSON feed for my Hugo site. I ran into some trouble getting the JSON output working right and want to thank @bep for helping me out on the forums. I based my solution on a new Hugo feature, Output Formats, but obviously I may have done this in a completely stupid manner as well. As always, I encourage folks to leave me a comment and let me know what could be improved.
ALright, so to begin, I created a file in /content
called jsonfeed.md
. This file is empty and just serves as a way to tell Hugo where to create the feed.
{
"outputs":["json"],
"layout":"feed"
}
Note that I'm specifying json as my output and I'm saying my layout will be the feed template. That's where the real work takes place:
{
"version": "https://jsonfeed.org/version/1",
"title": "{{ .Site.Title }}",
"home_page_url": "{{ .Site.BaseURL }}",
"feed_url": "{{ .Permalink}}",
{{ if isset .Site.Params "description" }}
"description": "{{ .Site.Params.description }}",
{{ end }}
{{ if isset .Site.Params "author" }}
"author": { "name": "{{ .Site.Params.author }}" },
{{ end }}
"items": [
{{ range $i, $e := first 10 .Site.Pages }}
{{ if $i }}, {{ end }}
{
"id": "{{ .Permalink }}",
"title": "{{ .Title }}",
"content_text": {{ .Summary | jsonify }},
"url": "{{ .Permalink }}",
"date_published": "{{ .Date }}",
"tags": ["{{ delimit .Params.tags "," }}"]
}
{{ end }}
]
}
Most of this should make sense I think - but let me call out some particular aspects. First, I've made description and author both optional based on whether or not the user has this in their Hugo settings.
In the range call, I had to use slightly weird syntax to support "include a comma except for the last iteration." This syntax makes zero sense to me, but I copied it from another support post by @bep located here. Even though the internal if
with the comma is at the top, it renders at the bottom. Yeah, ok.
As for each item, for the most part it just plain works as shown. Hugo summaries are plain text only so I used content_text
instead of content_html
. I run it through a pipe to make it safe for JSON. For tags, I was a bit torn. Hugo supports both categories and tags, and in theory you could use both I guess. Or just categories. I used tags just to keep it simple, but that's definitely something you would want to look into for your own Hugo site.
Finally, the date one rendered a bit weird on my blog. Let's look at the output to see the full response (I removed a bunch of items to make it shorter):
{
"version": "https://jsonfeed.org/version/1",
"title": "Raymond Camden",
"home_page_url": "http://localhost:1313/",
"feed_url": "http://localhost:1313/jsonfeed/index.json",
"author": { "name": "Raymond Camden" },
"items": [
{
"id": "http://localhost:1313/2017/05/18/creating-a-json-feed-for-hugo/",
"title": "Creating a JSON Feed for Hugo",
"content_text": "Recently a new specification was launched to recreate RSS in JSON, JSON Feed. For folks who may not be aware, RSS is an XML spec (well, multiple ones) for sharing content between sites. Blogs, primarily, and content-heavy sites typically make use of this. I\u0026rsquo;m not sure how many people outside of developers actually use RSS, but it\u0026rsquo;s still definitely a \u0026ldquo;thing\u0026rdquo; even if you don\u0026rsquo;t necessarily think of it when thinking about APIs.",
"url": "http://localhost:1313/2017/05/18/creating-a-json-feed-for-hugo/",
"date_published": "2017-05-18 11:32:00 -0700 -0700",
"tags": ["hugo"]
}
,
{
"id": "http://localhost:1313/2017/05/15/my-own-openwhisk-stat-tool/",
"title": "My Own OpenWhisk Stat Tool",
"content_text": "While waiting at the airport this past weekend, I worked on a little utility to help me retrieve information about my OpenWhisk actions. As you know (hopefully know!), Bluemix provides a \u0026ldquo;stats\u0026rdquo; page for your OpenWhisk stuff but it is a bit limited in terms of how far it goes back and doesn\u0026rsquo;t yet provide good aggregate data about your action. So for example, I really wanted to see how well my action was responding in a simple tabular fashion.",
"url": "http://localhost:1313/2017/05/15/my-own-openwhisk-stat-tool/",
"date_published": "2017-05-15 09:22:00 -0700 -0700",
"tags": ["openwhisk"]
}
,
{
"id": "http://localhost:1313/2017/04/28/bound-packages-openwhisk-and-web-actions/",
"title": "Bound Packages, OpenWhisk, and Web Actions",
"content_text": "Hey folks, this is just a warning to other users in case they run into the same issue I did. As you (may) know, OpenWhisk supports the idea of packages. Packages let you organize actions into a cohesive unit, much like packages in other languages/platforms. Packages can also have default parameters that apply to every action in the package.\nPackages can also be shared, which makes them callable by other users.",
"url": "http://localhost:1313/2017/04/28/bound-packages-openwhisk-and-web-actions/",
"date_published": "2017-04-28 09:08:00 -0700 -0700",
"tags": ["openwhisk"]
}
]
}
Notice the double -0700 -0700
? I'm pretty sure that's just an issue on my blog due to how I specify dates in my posts. I probably did it wrong, but with near 6000 posts, I'm not changing it. For my fix, I switched up the above code to:
"date_published": "{{ replace .Date "-0700 -0700" "-0700"}}",
And that's it. As I said, there's probably a better way of doing this, and I honestly don't know if anyone is going to even make use of JSON Feed, but if folks want mine, they can find it at https://www.raymondcamden.com/jsonfeed/index.json
Archived Comments
Interesting. I think i'm one of the few survivers still using RSS to follow the most interesting blogs instead of becoming crazy into the social media information overflow.
I was using some RSS reader a while back, but I literally forgot I had it. :\
Oops - looks like I forgot my online Hugo is a bit behind my local Hugo. That URL won't work till I fix that.
Fixed.
this is what i'm looking for... is it possible to have json from each content? with final output blog.com/myarticle-title/my...
No idea. :) To be honest, I find so much of Hugo difficult to work with I wish I could change. :\
Yes. In layouts / _default make single.json and that will be the template for the JSON version of individual posts.
Your tags are wrong. It is supposed to be an array of strings, i.e. "tags": ["RSS", "JSON", "Hugo"]. Please correct the post because it is very likely that people will copy/paste the post and it will lead to tags being wrong in many places…
To be honest, I completely forgot this was even a thing. I guess people are using JSONFeed which is good. ;) I'll look into correcting it.
Corrected.
thank you Carl :) I'll try add single.json template then.
Awesome! Thanks!
So did all the code get fixed. I need a reliable json solution. I am looking to migrate my hugo site to Wordpress.
Did what code get fixed?
Json Feed.
I want to export all my Hugo sites into one Json dump so that I can use a Json to Wordpress plugin to migrate all text data into WP.
It wasn't broken, was it?
You could try modifying my code. Removing the First filter should return all the content.