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
  • @rickincanada Thx for your tweet! Shoot us an email at asktheteam@keyholesoftware.com so we can set up a time to talk. Have a good day.
    July 24, 2014 at 3:33 PM
  • Never used JAXB? Check out a simple usage pattern that pairs #JAXB’s data binding capabilities with JPA - http://t.co/Ki9G04HV5e
    July 24, 2014 at 9:53 AM
  • Guess what today is? Tell An Old Joke Day - http://t.co/835ORWMX6N! So, why do programmers always confuse Halloween & Xmas? 31 Oct = 25 Dec
    July 24, 2014 at 8:45 AM
  • MT @midwestio: Posted another #midwestio talk recording to our YouTube channel: @MinaMarkham on modular CSS. Watch: http://t.co/aU3LpfUoi4
    July 24, 2014 at 8:25 AM
  • We just posted pictures from our National Hot Dog Day Lunch Cookout. Check them out - http://t.co/To06plaw1C
    July 23, 2014 at 4:14 PM
  • Good free cheat sheet - #Java Performance Optimization Refcard from @DZone: http://t.co/7vBgsmqy08
    July 23, 2014 at 10:48 AM
  • Did you know today is a holiday? It's National Hot Dog Day! We're gearing up for our team lunch hot dog cookout & can't wait to celebrate.
    July 23, 2014 at 9:43 AM
  • Check out our newest blog: #JAXB – A Newcomer’s Perspective, Part 1 http://t.co/Ki9G04HV5e
    July 22, 2014 at 1:22 PM
  • New post on the Keyhole blog by Mark Adelsberger: #JAXB – A Newcomer’s Perspective, Part 1 http://t.co/Ki9G04HV5e
    July 21, 2014 at 2:27 PM
  • If you're a Java dev, you're likely familiar with Annotations. But have you created your own #Java Annotations? Ex - http://t.co/BgCsYjxZKF
    July 18, 2014 at 12:10 PM
  • RT @gamasutra: Don't Miss: Unconventional Tips for Improving your Programming Skills http://t.co/6TFox7CKHU
    July 16, 2014 at 3:20 PM
  • We're about to send out our free monthly tech newsletter. Dev tips/articles via email. Not on the list yet? Sign up - http://t.co/F8h0NSiicZ
    July 15, 2014 at 11:57 AM
  • Have you ever tried creating your own #Java annotations? See a situation where it was beneficial - http://t.co/BgCsYjxZKF
    July 15, 2014 at 8:36 AM
  • There's a new post on the Keyhole blog by @jhackett01: Creating Your Own #Java Annotations - http://t.co/BgCsYjxZKF
    July 14, 2014 at 1:43 PM
  • We love development! Have you seen our weekly team blog? We show how to be successful with the tech we use. See it - http://t.co/nlRtb1XNQH
    July 12, 2014 at 2:35 PM
  • Rapid appdev has a bad rep, but there are ways to bring development time down the right way. Don't Fear the Rapid - http://t.co/aTPcAKOj0r
    July 11, 2014 at 3:10 PM
  • Automated Testing is great for dev, but does bring a set of challenges (especially for #agile teams). Success tips: http://t.co/1acl1ngO7i
    July 11, 2014 at 12:16 PM
  • This is fantastic - One small step for Google, one giant leap for empowering girls to code: http://t.co/R90V5DBkv1
    July 10, 2014 at 2:52 PM
  • #RabbitMQ: messaging software built on AMQP protocol. Learn relevant concepts & how to avoid common "gotchas" here: http://t.co/ZwMXlhKyX8
    July 10, 2014 at 9:31 AM
  • A huge welcome to Brian Wetzel who joins the Keyhole team today!
    July 10, 2014 at 8:50 AM
Keyhole Software
8900 State Line Road, Suite 455
Leawood, KS 66206
ph: 877-521-7769
© 2014 Keyhole Software, LLC. All rights reserved.