Archives
-
All those moments will be lost in time... like tears in rain... Time to die.
IE7 has been out for two and a half years. It is time to stop living in the past and retire IE6.
IETester
As an aside I highly recommend IETester (see the screenshot above) to anyone needing to test websites in old versions of IE. It renders IE 5.5, 6, 7 and 8 side by side in one application. It even supports tabs!
-
Add Twitter to your blog the right way with caching
Twitter (read more about Twitter here) offers a simple JavaScript API to add your status updates to your blog or other website.
The Problem
Injecting HTML into a webpage like this works well enough for the most part put it can produce problems if the website you are getting HTML from, Twitter in this case, is slow or unreliable. An inline script reference will hang the rendering of the page at that point until a response is returned.
If Twitter.com is down, not an uncommon occurrence, and you have an inline script reference like the one above then your website is essentially broken until Twitter fixes itself.
To get around this problem Twitter advises that the script reference should be placed at the end of a page. That fixes one issue but creates another: The content from Twitter pops into the page only once the page is completely loaded. It looks a little hacky at best and could cause the content on the page to suddenly move and reflow depending upon where the Twitter statuses are being inserted.
An example of content pop-in can be seen in the sidebar of MajorNelson’s blog. Each time the page reloads there is a noticeable delay before the Twitter statues are populated.
The Solution
My solution is simple: cache the result from Twitter in a cookie. Placing the script reference at the bottom of the page gets around the issue of a broken website and caching the result means no content pop-in after the first page view.
The JavaScript is pretty simple.
<script type="text/javascript">
function setCookie(name, value, expires) {
document.cookie = name + "=" + escape(value) + "; path=/" + ((expires == null) ? "" : "; expires=" + expires.toGMTString());
}
function getCookie(name) {
if (document.cookie.length > 0) {
var start = document.cookie.indexOf(name + "=");
if (start != -1) {
start = start + name.length + 1;
var end = document.cookie.indexOf(";", start);
if (end == -1) {
end = document.cookie.length;
}
return unescape(document.cookie.substring(start, end));
}
}
return "";
}
function twitterCachedCallback(c) {
// this will create the HTML. Function found inside blogger.js
twitterCallback2(c);
var content = document.getElementById("twitter_update_list").innerHTML;
// expire cookie after 30 minutes
var exp = new Date();
exp.setTime(exp.getTime() + (1000 * 60 * 30));
setCookie('twitter_content', content, exp);
}
// set content immediately if cached
var cachedContent = getCookie('twitter_content');
if (cachedContent) {
document.getElementById("twitter_update_list").innerHTML = cachedContent;
}
</script>
Add this script immediately after the Twitter content. If cached content is found then it will be inserted into the page straight away, improving user experience.
The only other change that needs to be made is to the script reference to Twitter. Rename the callback function from twitterCallback2 to twitterCachedCallback in the querystring and you’re done.
I use this technique on my blog (look to the sidebar on the right). You can also see an example webpage I threw together here.
-
Good (Date)Times with Json.NET
Latest version of this guide: Serializing Dates in JSON
---
I’ve said it before, I’ll say it again: Dates in JSON are hard.
The problem comes from the JSON spec itself, there is no literal syntax for dates in JSON. The spec has objects, arrays, strings, integers and floats, but it defines no standard for what a date looks like.
The default format used by Json.NET for dates is the same one used by Microsoft: "\/Date(1198908717056)\/". You can read more about it here.
JsonConverters
With no standard for dates in JSON, the number of possible different formats when interoping with other systems is endless. Fortunately Json.NET has a solution to deal with reading and writing custom dates: JsonConverters. A JsonConverter is used to override how a type is serialized.
public class LogEntry
{
public string Details { get; set; }
public DateTime LogDate { get; set; }
}
[Test]
public void WriteJsonDates()
{
LogEntry entry = new LogEntry
{
LogDate = new DateTime(2009, 2, 15, 0, 0, 0, DateTimeKind.Utc),
Details = "Application started."
};
string defaultJson = JsonConvert.SerializeObject(entry);
// {"Details":"Application started.","LogDate":"\/Date(1234656000000)\/"}
string javascriptJson = JsonConvert.SerializeObject(entry, new JavaScriptDateTimeConverter());
// {"Details":"Application started.","LogDate":new Date(1234656000000)}
string isoJson = JsonConvert.SerializeObject(entry, new IsoDateTimeConverter());
// {"Details":"Application started.","LogDate":"2009-02-15T00:00:00Z"}
}
Simply pass the JsonConverter you wish to use to the Json.NET serializer.
JavaScriptDateTimeConverter
The JavaScriptDateTimeConverter class is one of the two DateTime JsonConverters that come with Json.NET. This converter serializes a DateTime as a JavaScript Date object.
new Date(1234656000000)
Technically this is invalid JSON according to the spec but all browsers, and some JSON frameworks including Json.NET, support it.
IsoDateTimeConverter
IsoDateTimeConverter seralizes a DateTime to an ISO 8601 formatted string.
"2009-02-15T00:00:00Z"
The IsoDateTimeConverter class has a property, DateTimeFormat, to further customize the formatted string.
One final thing to note is all date values returned by Json.NET are in UTC time.
JSON makes working with dates harder than it needs to be but with a little work it is a problem that is easily solved. Good times.
-
Writing JSON to a file using Json.NET
There are a couple of different ways you can write to a file using Json.NET. The first is really simple:
Person person = GetPerson();
string json = JsonConvert.SerializeObject(person, Formatting.Indented);
File.WriteAllText(@"c:\person.json", json);
In this C# example we get the string returned from SerializeObject and pass it to WriteAllText, a helper method on the very useful System.IO.File class. The JSON contents of the string is written to the file.
If greater control is required over how the file is written, or the JSON you are writing is large and you don’t want the overhead of having the entire JSON string in memory, a better approach is to use JsonSerializer directly.
Person person = GetPerson();
using (FileStream fs = File.Open(@"c:\person.json", FileMode.CreateNew))
using (StreamWriter sw = new StreamWriter(fs))
using (JsonWriter jw = new JsonTextWriter(sw))
{
jw.Formatting = Formatting.Indented;
JsonSerializer serializer = new JsonSerializer();
serializer.Serialize(jw, person);
}
More code but also more efficient: the JSON is written directly to the file stream. The other bonus to using this approach is that it can be adapted to write to any .NET Stream: file streams, web response streams, memory streams, etc.
Isn’t polymorphism cool? [:)]
-
Googleman
Googleman is a fun and pointless application for launching Google.com in the style of the Batman 60s TV show, complete with spinning Google logo and sound effects. Now when a friend or co-worker says “To Google!” you can open your favourite search engine in style.
Launch Google either from the Googleman tray icon or by hitting the Windows+G hotkey.
Video Preview
Technical Stuff
Googleman is my first WPF application and it is extremely simple. The Batman 60s splash screen effect is achieved by launching a fullscreen window with two images. The background image is a screenshot of the desktop with a rotate animation applied to it. The logo zoom is done using a scale animation. Once the splash screen animation is complete the window hides itself and launches Google.com.
Kind of ruins the magic when you know how it works.
Background
We have been discussing patterns a fair bit at work so I wrote Googleman using the MVP pattern. I am working on an ASP.NET MVC website at the moment and Googleman was so I could understand the differences between the two patterns. Hopefully I have gotten it right…
Thanks to JD for help with testing and Tokes for insights into MVP.
Disclaimer
Google is serious business. Googleman is not a toy and should only be used in serious business situations. Or for lulz.
-
Teach a man to Google
Give a man a link, you have answered him for today. Teach a man to Google, and he will stop bothering you for a lifetime.
- James Newton-King*, 2009
-
A .NET Open Source Project Retrospective: Json.NET
I have always enjoyed the video game post-mortem articles on Gamesutra. Written by developers for developers, they reflect on how the project went, what they learnt and what they would do differently. Hearing peoples thoughts on what they have created is very interesting to me (I *love* DVD commentries) and have never seen the same done for an open source project.
This post on Json.NET, a retrospective rather than a post-mortem since Json.NET is still alive, follows the same format as Gamesutra: What went right and what went wrong and lessons I have learnt along the way.
Background
Json.NET grew out of projects I was working on in late 2005 involving JavaScript, AJAX and .NET. At the time there were no libraries for working with JSON in .NET so I began to grow my own.
This is the first open source project I publically released and the most popular with over 50,000 downloads.
What Went Right?
1. .NET
The whole purpose was to write a JSON library for .NET, so there was never a choice of which platform to use. However in saying that, .NET has been great to work with. The base class library and the C# programming language are well thought out and are (mostly) a joy to work with. The .NET framework has gotten better with every new release.
The .NET tools stack I use with Json.NET likewise provides an excellent experience. Visual Studio when combined with NUnit, TestDriven.NET and VisualSVN allows for rapid testing and development. Coding, compiling, testing, debugging and manage source control all within the IDE is a great boon for productivity.
2. Incremental releases
I like the quote ‘incremental improvement is better than delayed perfection’. The 1.0 release of Json.NET came from just a couple of weekends of effort and after a flurry of releases to fix the initial bugs, a new major version of Json.NET has been released approximately every 6 months.
While not every feature I wanted was in version 1.0, users soon gave feedback on what they wanted most and I went from there. I haven’t been overly precious about maintaining 100% compatibility between releases but the core behaviour of Json.NET: reading, writing and serializing between .NET and JSON has remained unchanged since day one. New features like LINQ to JSON have slotted in side by side with existing classes.
3. User driven design
Confession time: I have not used JSON in anger on a project in years. Almost every change, bug fix and new feature since Json.NET 1.0 has come from user feedback.
Although it sounds like a negative, I think it turned out to be a great way to develop. The end result is a focus on what users want most rather than relying on your own experiences.
I think a danger to keep in mind with this approach is that user feature requests are generally very specific and will only address one persons need. Make sure you take care to take a step back and look at their intent and then create a solution that works for everyone.
4. Unit Testing
Json.NET, back in 2005, was the first project I used unit testing on and it has been a great success (although the test project is a bit of a mess…). Good unit testing coverage over the entire project has meant I could make wholesale changes to the internal workings of Json.NET to support a new feature while having a good level of security that I wasn’t breaking what was already there.
Each time a bug is reported I have a grown into a pattern where I recreate the bug in a unit test, assert that it is broken, fix the bug and then assert that it is fixed. It is a good way to get coverage of all those edge cases you never thought of.
5. CodePlex
CodePlex is an open source project hosting website from Microsoft. Overall I have found it a great place to host releases, source control and discussion forums for Json.NET. These days it also has good Subversion support and it provides all kinds of interesting statistics.
Probably the thing I like most about using a site like CodePlex is that it makes source code public and easy to download in a zip file. When I deal with a bug and check in the updated files I can straight away tell a user that fixed source code is available and a link to where they can download it.
What Went Wrong?
1. Poor documentation
A well designed API and XML intellisense documentation isn’t enough for the average user. Until Json.NET 2.0 was released, which included CHM documentation, a lot of questions were asked about how to achieve very basic tasks.
When it comes to documentation, it is hard to ever have too much. I think getting started guides and high level overviews of a feature are especially useful to users.
2. No central knowledge base
Json.NET suffers from having too many places to search if you are looking for information… old and new blog posts, a project page, a forum on CodePlex and the CHM documentation. Not everyone is a Google ninja and expecting users to search out a reply you made to someone else asking the same question a couple of years ago is asking a lot.
Directing users to one place to look or ask for help is key. In future I might look to move a lot of Json.NET’s documentation to CodePlex and use its wiki functionality.
Conclusion
I’m really happy with how Json.NET has gone as a project. JSON’s popularity in programming has grown markedly over the last 3 years and Json.NET’s has grown with it. I look forward to what new possibilities .NET 4.0 might bring.
Stats
Numer of developers: 1
Budget: $0
Development Time: 2 1/2 years (and counting)
Initial Release Date: June 2006
Platform: .NET, Silverlight, Compact Framework
Software: Visual Studio, NUnit, TestDriven.NET, VisualSVN, TortoiseSVN, Sandcastle
Size of Project:
Files: 180
Lines of code (including tests): 21,000