I'm Lance Somoza, a professional IT Consultant with over 15 years of industry experience and an obsession for technology. This is my tech soapbox.

Gaddgict JSON Feed (Updated 5.27)

Many know what an RSS feed is, or have at least seen the icon before on a website. It offers a way for you to subscribe to a site’s posts via a third-party reader application. Gaddgict’s RSS feed can be found at https://gaddgict.com/feed/rss. RSS and Atom feeds have been the longtime-standard when it comes to aggregating posts.

Last week, I learned of a new feed spec called JSON Feed, thanks to John Gruber over at Daring Fireball. After checking it out, I couldn’t help myself and knew I was in for a fun little project. JSON (JavaScript Object Notation) itself is a simple way to organize data that can be easily read by applications. It’s lightweight, easy to read, understand, and put together. Popular feed readers have already started adopting JSON Feed (like NewsBlur), even though it’s in its infancy. Maxime Vaillancourt put together the first JSON Feed Viewer at https://json-feed-viewer.herokuapp.com, with a steadily-growing list of showcased feeds (you’ll find Gaddgict’s on there).

The direct link to Gaddgict’s JSON feed is https://gaddgict.com/feed/json. Now, for all the technical details behind how I implemented it…

The JSON feed spec itself was published by Manton Reece and Brent Simmons. Now, Gaddgict runs on HTMLy, an open-source, flat file1 platform. I love HTMLy’s simplicity, which offers a real get-your-hands-dirty invitation.

I first took a look at HTMLy’s code for generating the RSS feed, which calls a PHP function to grab all the ‘x’ latest posts and outputs them into the appropriate format (see below).

// Turn an array of posts into an RSS feed
function generate_rss($posts)
{
    $feed = new Feed();
    $channel = new Channel();
    $rssLength = config('rss.char');

    $channel
        ->title(blog_title())
        ->description(blog_description())
        ->url(site_url())
        ->appendTo($feed);

    foreach ($posts as $p) {

        if (!empty($rssLength)) {
            if (strlen(strip_tags($p->body)) < config('rss.char')) {
                $string = preg_replace('/\s\s+/', ' ', strip_tags($p->body));
                $body = $string . '...';
            } else {
                $string = preg_replace('/\s\s+/', ' ', strip_tags($p->body));
                $string = substr($string, 0, config('rss.char'));
                $string = substr($string, 0, strrpos($string, ' '));
                $body = $string . '...';
            }
        } else {
            $body = $p->body;
        }

        $item = new Item();
        $cats = explode(',', str_replace(' ', '', strip_tags(remove_accent($p->category))));
        foreach ($cats as $cat) {
            $item
                ->category($cat, site_url() . 'category/' . strtolower($cat));
        }
        $item
            ->title($p->title)
            ->pubDate($p->date)
            ->description($body)
            ->url($p->url)
            ->appendTo($channel);
    }

    echo $feed;
}

Perfect, I thought. I should be able to just replicate the function (with a few tweaks), and output it as JSON, with the right structure. I’ll still use $rssLength so it outputs the same amount of posts as the RSS feed, but I’ll need to define some new arrays to make sure everything is in the right order.

First, the header array (the main part of the feed). This part identifies the feed, per spec.

$feedHeader = array(
'version' => 'https://jsonfeed.org/version/1',
'title' => 'Gaddgict',
'home_page_url' => 'https://gaddgict.com', 
'feed_url' => 'https://gaddgict.com/feed/json',
'description' => 'Gaddgict.com: a personal tech blog by Lance Somoza.',
  'author' => array(
  'url' => 'https://twitter.com/syrinxstarman',
  'name' => 'Lance Somoza'
   ),
'icon' => 'https://gaddgict.com/favicon/ms-icon-310x310.png',
'favicon' => 'https://gaddgict.com/favicon/apple-icon-72x72.png'
);

Alright, now the fun part! Let’s define a few more arrays. $item holds all the individual posts created in the $items array.

//define needed arrays for items and content.
$item = array();
$items = array();

This next part is similar to the RSS feed, but I won’t be indicating the category (channel), and I’ll be defining each title, body content, publication date, and url. 5.27 Update: I’m defining $link as well, for posts that have a featured external URL.

foreach ($posts as $p) {
if (!empty($rssLength)) {
  if (strlen(strip_tags($p->body)) < config('rss.char')) {
  $string = preg_replace('/\s\s+/', ' ', strip_tags($p->body));
  $body = $string . '...';
  } else {
     $string = preg_replace('/\s\s+/', ' ', strip_tags($p->body));
     $string = substr($string, 0, config('rss.char'));
     $string = substr($string, 0, strrpos($string, ' '));
     $body = $string . '...';
  }
$title = $p->title;
$body = $p->body;
$pubdate = $p->date;
$url = $p->url;
$link = $p->link; //added 5.27.17

5/27 Update: let’s convert the HTML entity for single quotes to its unicode format for the post titles. I had to do this because my single quotes weren’t displaying correctly in the feed readers (only content_html can have HTML according to the JSON Feed spec). Also, I could not get html_entity_decode() to work, which I believe should do the same thing. Anyway…

//Replace HTML entity for single quotes
$newTitle = str_replace("'", "'", $title);

Now I thought about this next part a bit, and it’s just a personal preference in how you want your content displayed in a feed reader. I decided that for longer posts, I would truncate them and include a “Read more” link. HTMLy uses the <!-- more--> tag in posts to specify this already, so this next part searches the body content for the tag, and if found, truncates the content and adds the “Read more” link.

//If a Read More tag is present, truncate the story and tack on a Read More link to the feed.
if (strpos($body,'<!-- more-->') != false)
{
  $body = strstr($body,'<!-- more-->', true);
  $readmore = "<a href=\"" . $url . "#more\">Read More</a>";
   $body = $body . $readmore;
}

The JSON Feed spec uses the RFC 3339 time format for publication date, so let’s convert to it from Unix epoch time.

//convert date to RFC 3339
$datetime = date("c",$pubdate);

Now with the housekeeping done on the posts, let’s add each one to the array. 5.27 Update: the code below replaced my initial implementation, since I now factor in external article links for posts that have one, defined as $link. I’m sure there is a more elegant way to do this, but it does the trick.

if(empty($link))
{               
   //Add all posts to the array.
   $items[] = array("title" => $newTitle, "date_published" => $datetime,
   "id" => $url, "url" => $url,
   "author" => array("name" => "Lance Somoza"),
   "content_html" => $body);
}
else {
  $items[] = array("title" => $newTitle, "date_published" => $datetime,
  "id" => $url, "url" => $url, "external_url" => $link,
  "author" => array("name" => "Lance Somoza"),
  "content_html" => $body); 
}

After all the latest posts are in, let’s make them part of the overall $item array I talked about earlier. Then, let’s join the header array with all the posts.

//Put all the posts into the item array.
$item = array('items' => $items);
//Merge the header and all posts.
$encode = array_merge($feedHeader,$item);

Last step! With everything accounted for, let’s set the page header to JSON and use PHP’s built-in json_encode() function to output. JSON_PRETTY_PRINT makes everything look nice and neat, while JSON_UNESCAPED_SLASHES removes slashes for normally-escaped characters (mostly due to hyperlinks).

//set the header to JSON and encode the entire feed
header('Content-type: application/json');
echo json_encode($encode, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);

This was a fun little project, and I’m really happy with the results. With a low time investment and its simplicity, I hope JSON Feed goes far.

Edited for formatting.


  1. Flat file systems use only individual files for their structure, content, and settings, as opposed to actual databases. ↩︎

Tags