Marking up json-ld

JSON-LD is a useful way to add semantic mark up your output with schema.org objects. From their site:

JSON-LD is a lightweight Linked Data format. It is easy for humans to read and write. It is based on the already successful JSON format and provides a way to help JSON data interoperate at Web-scale. JSON-LD is an ideal data format for programming environments, REST Web services, and unstructured databases such as CouchDB and MongoDB.

I like this better than marking up with interspersed itemscope, itemtype or itemprop, since it’s all in one block and just easy to read.

Here’s how I’m doing it. I hope it helps someone.

Given a config.toml with:

+++
...
ISO8601 = "2006-01-02T15:04:05JST"
...
+++

… specifying my timezone JST (Japan time, noting that server is also set to this), and a partial template called in my <head> that is meant to render json-ld markup for a schema.org BlogPosting schema:

{{ "<!-- ENTERING partial seo-schema.html -->" | safeHTML }}
{{ if .IsPage }}
{{ if eq .Type "post" }}
<script type="application/ld+json">
{
    "@context" : "http://schema.org",
    "@type" : "BlogPosting",
    "articleSection" : "{{ .Section }}",
    "name" : "{{ .Title }}",
    "headline" : "{{ .Title }}",
    "description" : "{{ if .Description }}{{ .Description }}{{ else }}{{if .IsPage}}{{ .Summary }}{{ end }}{{ end }}",
    "inLanguage" : "en-US",
    "author" : "{{ .Params.author }}",
    "creator" : "{{ .Params.author }}",
    "accountablePerson" : "{{ .Params.author }}",
    "copyrightHolder" : "{{ .Params.author }}",
    "copyrightYear" : "{{ .Date.Format "2006" }}",
    "datePublished": "{{ .PublishDate.Format $.Site.Params.ISO8601 }}",
    "dateModified" : "{{ .Date.Format $.Site.Params.ISO8601 }}",
    "url" : "{{ .Permalink }}",
    "wordCount" : "{{ .WordCount }}",
    "image" : [ {{ range $i, $e := .Params.images }}{{ if $i }}, {{ end }}{{ $e }}{{ end }} ],
    "keywords" : [ {{ range $i, $e := .Params.tags }}{{ if $i }}, {{ end }}{{ $e }}{{ end }} ]
}
</script>
{{ end }}
{{ end }}
{{ "<!-- LEAVING partial seo-schema.html -->" | safeHTML }}

… then, with a post markdown file yaml frontmatter like this, with the default hugo new date format including a +09:00 to indicate timezone:

---
author: Rick Cogley
authorlink: /about
authortwitter: https://twitter.com/rickcogley
banner: https://farm5.staticflickr.com/4059/5135448447_95b026227b_o.jpg
date: 2015-05-08T12:33:19+09:00
publishdate: 2015-05-08T12:33:19+09:00
description: The rsync that Apple provides in OS X Yosemite is out of date, but you can easily upgrade it using the homebrew package manager - a post by Rick Cogley.
draft: "false"
showauthor: "true"
showcomment: "true"
showdate: "true"
showpaging: "true"
showreadingtime: "true"
showsocialsharing: "true"
showtoc: "true"
showtotop: "true"
slug: upgrade-outdated-rsync-on-osx
subtitle: Homebrew to the rescue
tags:
- brew
- tap
- rsync
- homebrew
- upgrade
- osx
- mac
title: Old rsync on OS X?
topics:
- Tips
- SysAdmin
- Upgrades
news_keywords:
- brew
- tap
- rsync
- homebrew
- upgrade
- mac
- osx
images:
- /img/homebrew.png
- http://static.cogley.info/img/rick-cogley-avatar-900x900.jpg
---

… I get this as the result, in view source:

<script type="application/ld+json">
{
    "@context" : "http://schema.org",
    "@type" : "BlogPosting",
    "articleSection" : "post",
    "name" : "Old rsync on OS X?",
    "headline" : "Old rsync on OS X?",
    "description" : "The rsync that Apple provides in OS X Yosemite is out of date, but you can easily upgrade it using the homebrew package manager - a post by Rick Cogley.",
    "inLanguage" : "en-US",
    "author" : "Rick Cogley",
    "creator" : "Rick Cogley",
    "accountablePerson" : "Rick Cogley",
    "copyrightHolder" : "Rick Cogley",
    "copyrightYear" : "2015",
    "datePublished": "2015-05-08T12:33:19JST",
    "dateModified" : "2015-05-08T12:33:19JST",
    "url" : "http:\/\/localhost:1313\/post\/upgrade-outdated-rsync-on-osx",
    "wordCount" : "214",
    "image" : [ "/img/homebrew.png", "http://static.cogley.info/img/rick-cogley-avatar-900x900.jpg" ],
    "keywords" : [ "brew", "tap", "rsync", "homebrew", "upgrade", "osx", "mac" ]
}
</script>

Amongst other semantic formats, at least the above source validates in Schema Markup Testing Tool | Google Search Central  |  Google for Developers.

With the v0.14-DEV version, you can use a new function absURL.

"image" : {{ apply .Params.images "absURL" "." }}

This serves to add the baseurl to the path of the image in the array, if the image is just a relative path.

1 Like

FWIW, I found it a real pain to get all the tags configured in JSON form, nested the way the Google Structured Data tester wanted them, and properly escaped, so I just used the more classic HTML tag format.

You can see the working Schema here on GitHub, and live on https://Lustforge.com

Some notes:

  • Your thumbnail image can’t be a vector image
  • This is no automatic list of images, so I added a “summary” and “image” props to my posts
  • The output validates with Google’s Structured Data Test Tool
2 Likes

This fixes the / issue in "url":

```“url”: {{ printf “%s” .Permalink }},` (also notice how the surrounding quotes have been omitted)

H/T @eywu and @MooreReason

Thanks for sharing. Have you found a good way to scrape your own pages to dynamically generate the JSON-LD at the top-level of a page? Also, I found .PublishDate on its own is unreliable. Here’s how I’m outputting my publish dates. Hope it helps!

{{ dateFormat "2006-01-02T15:04:05-07:00" (default .Date (.PublishDate)) }}

Thank you! Very helpful

I written a tutorial to add schema.org (WebSite and BlogPosting) markup using JSON-LD.

1 Like

Hi all,

I’m facing the problem of repeated events with Schema.org. We provide these kinds of events: https://nutspubcrawl.com/the-tours/camden-pub-crawl/ in different languages.

Is there a way to display them properly with Json. Here is our source : https://github.com/jeremielondon/nuts-hugo

Many thanks for your help.

Best,

@jeremielondon I got your DM but let’s discuss in public so others may benefit.

In my opinion a major point to using json-ld is to let Google show the structured markup on the site in search results (e.g. rich cards), so, I would want to know what Google specifies for the markup of such repeated events.

It appears Google wants you to mark up repeated events individually, per this old post. That implies generating a page for each, and working the translations into both the html code block and whatever json-ld block you need.

It also means scheduling site generation so that said pages can be re-generated with the most recent info.

There are some “moving parts” to get this working I think.

I’ve started to make a solution that is easier to just drop in and work. It’s work in progress at the moment, and any help would be appreciated: https://github.com/martijn00/HugoStructuredData

It feels a bit weird that there is no way in Hugo to make this a drop in plugin coming from a .NET background. Not sure where the discussion on plugins for Hugo is at the moment, but i would be interested in that.

1 Like

I’ve started working on the schema.org data for my theme. It is a lot more complex that I’d even realised at first. And some things don’t fit well into json-ld so I also need to look at microdata. What a pain.

I took the time to painstakingly mark up some Japanese pages with events on them, exactly per Google’s spec, with perfect validation. However, although the site scores well in rankings generally, the markup was completely ignored by Google, and I never did get any “rich results” like advertised. If you’re outside the US, it might pay to dip a toe in to check whether it actually works / leads to any benefit. I gave up doing it, after 6 months of nothing. And this was for an important medical organization.

1 Like

Thanks Rick, useful info. Especially for those coming to this from a commercial viewpoint.

Google specifically state that they do not guarantee that json-ld/microformat markup will give rich results, only that it makes it somewhat more likely.

If it only boosts search rank a little for some posts, it is probably worth it in the long run. Be nice if Google/Bing/Facebook/Twitter used a single, agreed set of microdata though!

As this is a gradual improvement of my theme, I’ll continue to work on it in the background as time and energy allows.

1 Like

I’m super reluctant to give any kind of guarantee whatsoever about “what will happen” when referring to Google. Sometimes it’s a real crapshoot!

1 Like

what a nice share. i will try in next few day. thanks a lot for the post. i will catch up the update later

or maybe the feature was just unreleased yet for outside country. :slight_smile:

Thing is, other companies are showing rich format, in the Japanese search.

I generated my blog website (https://codingnconcepts.com/) using hugo and, then I struggled a lot to find some decent tutorial to add structure data (json-ld) to my blog website’s home page and other blog post pages.

Finally i did it on my own and wrote an article to help others:

4 Likes

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.