Quantum
Quest

Algorithms, Math, and Physics

Integrating a custom JSON-LD loader

As part of my ongoing effort to seamlessly integrate my blog into the overall layout of my site, I’ve taken a step further by incorporating a custom-made JSON-LD loader. This loader is designed to enhance the structured data of my blog posts, making them more discoverable and meaningful to search engines.

The JSON-LD loader

The loader I developed is not just any ordinary loader; it’s tailored to fit the unique structure and requirements of my blog. The implementation is straightforward yet effective, utilizing my custom-made loader mechanism. For those interested in the technical details, the loader leverages the power of JSON for linking data, providing a structured format that is both easy to understand and use.

You can find more details about the loader and its integration here.

Integrating with Hexo

Hexo, the engine behind my blog, offers a flexible and powerful platform for blog creation. To integrate the JSON-LD functionality, I added a script to Hexo that introduces a new generator. This is done by registering a generator with Hexo as follows:


const path = require('path');

hexo.extend.generator.register('json-ld', function (locals) {
  return locals.posts.map(post => {
    const datePrefix = post.date.format('YYYYMMDD');
    const fileName = `${datePrefix}_${post.slug}.json`;
    const outputPath = path.join(post.path, fileName);
    let tagNames = '[]'; // Default to an empty JSON array
    if (post.tags && post.tags.length) {
      tagNames = JSON.stringify(post.tags.toArray().map(tag => tag.name));
    }

    let categoryNames = '[]'; // Default to an empty JSON array
    if (post.categories && post.categories.length) {
      categoryNames = JSON.stringify(post.categories.toArray().map(cat => cat.name));
    }

    // Generate JSON content using ejs template
    let jsonContent = hexo.render.renderSync({path: 'themes/<my_theme>/layout/_partials/json-ld.ejs', engine: 'ejs'}, {
      page: {
        path: post.path,
        date: new Date(post.date).toISOString(),
        updated: new Date(post.updated).toISOString(),
        lastModified: post.lastModified ? new Date(post.lastModified).toISOString() : new Date(post.date).toISOString(),
        title: post.title,
        content: post.content,
        excerpt: post.excerpt,
        author: post.author || hexo.config.author,
        image: post.image || hexo.config.default_image,
        categories: categoryNames,
        tags: tagNames
      },
      config: hexo.config
    });

    return {
      path: outputPath,
      data: jsonContent
    };
  });
});

This generator automatically creates a JSON-LD structured data snippet for each post, utilizing a partial template located under themes/<my_theme>/layout/_partials/json-ld.ejs:


<%
var postUrl = config.url + '/' + page.path + 'index.html';
var postDate = new Date(page.date).toISOString();
var lastModifiedDate = page.lastModified ? new Date(page.lastModified).toISOString() : new Date(page.date).toISOString();
var authorName = page.author || config.author;
var wordCount = page.content.split(/\s+/).length;
%>
{
  "@context": "http://schema.org",
  "@type": "BlogPosting",
  "headline": "<%= page.title %>",
  "image": "<%= page.image || config.default_image %>",
<% if (page.categories && page.categories !== '[]') { %>
  "genre": <%- page.categories %>,
<% } %>
<% if (page.tags && page.tags !== '[]') { %>
  "keywords": <%- page.tags %>,
<% } %>
  "wordcount": "<%= wordCount %>",
  "publisher": {
    "@type": "Organization",
    "name": "<%= config.publisher_name %>",
    "url": "<%= config.url %>",
    "logo": {
      "@type": "ImageObject",
      "url": "<%= config.logo %>"
    }
  },
  "url": "<%= postUrl %>",
  "datePublished": "<%= postDate %>",
  "dateCreated": "<%= postDate %>",
  "dateModified": "<%= lastModifiedDate %>",
  "description": "<%= page.excerpt %>",
  "articleBody": "<%- JSON.stringify(page.content.replace(/<[^>]*>/g, '')).slice(1, -1) %>",
  "author": {
    "@type": "Person",
    "name": "<%= authorName %>",
    "url": "auth",
    "image": "<%= config.author_image %>"
  },
  "mainEntityOfPage": {
    "@type": "WebPage",
    "@id": "<%= postUrl %>"
  }
}

Configuration adjustments

To accommodate the new JSON-LD feature, a few fields, such as default_author, need to be added to Hexo’s configuration. These fields ensure that the generated JSON-LD data accurately reflects the metadata of each blog post, contributing to better search engine visibility and data interpretation.

The addition of a custom JSON-LD loader to my blog marks another milestone in its evolution. By leveraging structured data, my blog posts are not only more informative but also better positioned in the digital ecosystem. Stay tuned for more updates as I continue to enhance my site’s functionality and user experience.

The layout customization is nearly complete, with only the navigation and customization of the archive/tags/categories page containers remaining on my to-do list.