Google Reader

A Little HTML5: Using JSON and File API to View Starred Google Reader Posts

by on May 26, 2013 10:00 am

A couple of months ago, Google announced that it was retiring its web feed reader Google Reader. As a long-time user of the service, I was disappointed. But there are a couple of substitutes out there like FeedlyThe Old ReaderNetVibes, etc. Google gives you the option of exporting your data from Reader through Google Takeout. This is where my inspiration for a little project started.

When you go through the process to export your data through Takeout, you receive a zip file. It contains an XML file with all of your subscriptions in OPML format, as well as 7 JSON files containing information like your social connections, stories you “liked,” stories you shared with others, notes you took about stories, stories that you put in your favorites list, etc. The problem I found is that none of the above mentioned substitutes know what to do with any of these JSON files.

I typically used my favorites list (the “starred” items) as an “I’ll read this later” group. But now, I have a 1.5 MB file of saved items and no way to read them.

So, let’s create a way to read them using JSON and the File API!

The Solution

google-reader-import.html

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="robots" content="noindex">
        <title>Google Reader Import</title>
        <link type="text/css" rel="stylesheet" href="google-reader-import.css">
    </head>
    <body>
<h1>Google Reader Import</h1>
        <input type="file" id="fileInput" />
        <div id="stories" class="stories"></div>
        <script src="google-reader-import.js">

        </script>
    </body>
</html>

I also created a CSS file to style the posts similarly to how they are presented in the original Google Reader application:

google-reader-import.css

.stories {
    margin: 10px 0px 0px;
    max - width: 650px;
}
    .entry - main {
    padding: 5px 10px 0px 13px;
    margin - bottom: 20px;
    border - top: 0px none;
    border - bottom: 0px none;
    background: none repeat scroll 0 % 0 % rgb(247, 247, 247);
    border: 1px solid rgb(233, 233, 233);
    min - height: 20px;
    padding: 19px;
    margin - bottom: 20px;
    background - color: rgb(245, 245, 245);
    border: 1px solid rgb(227, 227, 227);
    border - radius: 4px 4px 4px 4px;
    box - shadow: 0px 1px 1px rgba(0, 0, 0, 0.05) inset;
}
    .entry - date {
    float: right;
    color: rgb(102, 102, 102);
    text - decoration: none;
    margin: 0px;
}
    .entry - title {
    color: rgb(17, 85, 204);
    font - size: 140 % ;
    margin: 0px;
    max - width: 650px;
}
    .entry - title - link {
    color: rgb(17, 85, 204);
    text - decoration: none;
}
    .entry - author {
    color: rgb(102, 102, 102);
    text - decoration: none;
    margin: 0px;
}
    .entry - source - title - parent {
    color: rgb(102, 102, 102);
}
    .entry - source - title {
    color: rgb(17, 85, 204);
    display: inline - block;
    text - decoration: none;
}
    .entry - body {
    color: rgb(0, 0, 0);
    padding - top: 0.5em;
    max - width: 650px;
    margin: 0px;
}
    .item - body {
    margin: 0px;
}

However, the real work is done in the JavaScript.

The first function I’ve named handleFileSelect. When the user selects a file, the handleFileSelect() function gets called. We use the JavaScript File API to read in the file, using the FileReader to handle actually asynchronously loading the JSON file. After creating the new FileReader object, we set up its onload function, then call readAsText() to start the read operation in the background. When the entire contents of the JSON file are loaded, we pass it to the JSON.parse() function in our onload callback. Our implementation of this routine takes the JSON objects and iterates through its item list, where each item represents a single post from our starred list.

The second function, createStoryEntry, creates the HTML for each post using a format similar to the original application. The date in the JSON is along in seconds, so we take it and multiply by 1000 to convert it to milliseconds and create a JavaScript Date object so we can create a user-friendly date representation.

Another thing to consider is that some posts have full content, while others just contain a summary of the full content. We do a quick check to determine if the post has full or partial content and then set our content to the correct value. Each post is then appended as a child to our “stories” div:

google-reader-import.js

function createStfunction createStoryEntry(jsonItem) {
    var publishedDate = new Date(jsonItem.published * 1000);
    var content = (typeof jsonItem.content === 'undefined') ? jsonItem.summary.content : jsonItem.content.content;
    var entryMain = document.createElement("div");
    entryMain.className = "entry-main";
    entryMain.innerHTML = '</pre>
<div class="entry-date">' + publishedDate.toDateString() + ' ' +
 publishedDate.toTimeString() + '</div>
<h2 class="entry-title">' +
 '<a class="entry-title-link" href="' + jsonItem.alternate.href + '" target="_blank">' +
 jsonItem.title + '</a></h2>
<div class="entry-author">' +
 '<span class="entry-source-title-parent">from <a class="entry-source-title" href="' +         jsonItem.origin.htmlUrl + '" target="_blank">' + jsonItem.origin.title +
 '</a></span></div>
<div class="entry-body">
<div>' +
 '
<div class="item-body">
<div>' + content + '</div>
</div>
' +
 '</div>
</div>
<pre>
';
    return entryMain;
}

function handleFileSelect() {
    var storiesContainer = document.getElementById('stories');

    var reader = new FileReader();
    reader.onload = function (evt) {
        var parsedStories = JSON.parse(evt.target.result);
        for (var i = 0; i < parsedStories.items.length; i++) {
            storiesContainer.appendChild(createStoryEntry(parsedStories.items[i]));
        }
    };
    reader.readAsText(this.files[0]);
}
document.getElementById('fileInput').addEventListener('change', handleFileSelect, false);

If you want to see more of what you can do with the File API, Mozilla has a great resource on adding additional features like drag-and-drop.

This particular program will only work with a modern browser, but you could also use a JavaScript library like Modernizr to determine if the browser has the capability to use the File API and JSON. Good luck!

— Brice McIver, asktheteam@keyholesoftware.com

  • Share:

Leave a Reply

Things Twitter is Talking About
  • Don't miss @jhackett01's newest post on the Keyhole blog - Processing #Java Annotations Using Reflection: http://t.co/E1lr3RmjI7
    September 15, 2014 at 12:02 PM
  • We're pretty excited - Keyhole's #BikeMS team raised 158% of their fundraising goal to benefit @MidAmericaMS. Plus, they had a great ride!
    September 15, 2014 at 10:38 AM
  • A huge welcome to David Kelly (@rheomatic) who officially joins the Keyhole team today! :-)
    September 15, 2014 at 10:00 AM
  • Sending warm thoughts to @eastlack, @cdesalvo, @wdpitt & all participating in #BikeMS this AM. Thanks for helping in the fight against MS!
    September 13, 2014 at 8:10 AM
  • .@rheomatic We are so excited to have you joining the team! Welcome :-)
    September 12, 2014 at 4:11 PM
  • As the official holiday is a Saturday, we're celebrating today! Happy (early) #ProgrammersDay to you! http://t.co/1CvUfrzytE
    September 12, 2014 at 1:55 PM
  • Tomorrow @cdesalvo, @eastlack, & @wdpitt are riding #BikeMS to benefit @MidAmericaMS. You can get involved, too - http://t.co/9boQwEUxth
    September 12, 2014 at 11:00 AM
  • RT @AgileDevs: 5 tips for great code reviews http://t.co/9PdbtEv0z8
    September 11, 2014 at 2:53 PM
  • The BEMs of Structuring #CSS - http://t.co/159suYtfx6 A quick introduction to the Block Element Modifier methodology.
    September 10, 2014 at 2:49 PM
  • A huge welcome to Joseph Post (@jsphpst) who has joined the Keyhole team this week!
    September 10, 2014 at 9:52 AM
  • @TheGrisExplores Absolutely, and thanks for the compliment! Here's an article that you might find helpful, too - http://t.co/7oxpaohCS1
    September 9, 2014 at 2:22 PM
  • Express.js seems to be the top pick! MT @TheGrisExplores: "what's your fave server-side MVC framework for NodeJS when SPA is not an option?"
    September 9, 2014 at 1:56 PM
  • RT @TheGrisExplores: Yo @KeyholeSoftware dudes and dudettes: what is your favorite server-side MVC framework for NodeJS when SPA is not an …
    September 9, 2014 at 1:15 PM
  • There is a new post on the Keyhole blog by @brianletteri - The BEMs of Structuring CSS: http://t.co/159suYtfx6
    September 9, 2014 at 12:12 PM
  • Know a bright grad? We're looking for a business support / recruiting representative to join our team in Kansas City: http://t.co/GDvFVmoMF9
    September 8, 2014 at 11:31 AM
  • A huge welcome to @jeremygard who joins the Keyhole team today!
    September 8, 2014 at 8:40 AM
  • 8 tips to help you code for maintainability so the next developer doesn't hate you (which we think is important) - http://t.co/Aoe931WLzb
    September 5, 2014 at 2:40 PM
  • Psst... we're looking to add to our team a Java dev who likes SPA technologies like #backbone & #angular - http://t.co/wdbZBFj4y2
    September 5, 2014 at 11:35 AM
  • A #JavaScript promise is an IOU to return a value in the future. Learn about promises - http://t.co/122CZm0fhk http://t.co/F0VorQcMML
    September 5, 2014 at 11:05 AM
  • RT @DZoneLinks: The ABCs of REST - http://t.co/T37lDRfREX - @DZoneLinks Big Link by bball
    September 4, 2014 at 2:26 PM
Keyhole Software
8900 State Line Road, Suite 455
Leawood, KS 66206
ph: 877-521-7769
© 2014 Keyhole Software, LLC. All rights reserved.