What should be in C#

Generic Indexers

C# has generic classes, interfaces, and methods. What about generic indexers?

Class indexers are properties (has a get accessor):

public object this[string key]
{
	get { return new Object(); }
}

Just as indexers are advantageous and so are generics, having generic indexers would be also advantageous:

public T this<T>[string key]
{
	get { return default(T); }
}

Properties cannot be generic in C#, ergo no generic indexers. While I haven’t needed a generic property yet (whether to use a property or method is mostly preferential, so a generic method can always be used instead), I have had scenarios where a generic indexer would be advantageous.

For example, wouldn’t it be cleaner to do this:

var settings = new Settings();
int timeout = settings<int>["CacheInMinutes"];

Than to have to unbox or convert, as is the case without generic properties, like this:

var settings = new Settings();
int timeout = int.Parse(settings["CacheInMinutes"]);

I, for one, think so. Why aren’t there generic properties? I’m not sure. A conscious language design decision? Some logistical conflict in the compiler? Maybe someone more informed knows.

Static Local Variables

This is one example where I think VB got it right and C# got it wrong.

Let’s say I have a class with a method, and that method needs a static variable. Only that method (one method) is going to use the static variable though:

	
/// <summary>
/// Uber complex class with all sorts of instance fields,
/// properties, static methods, interface methods, etc.
/// </summary>
public class Feed
{
	private static readonly string[] adultTerms = ...;

	public bool IsAdult
	{
		get { return Feed.IsAdultCheck(this); }
	}

	public static bool IsAdultCheck(Feed feed)
	{
		foreach(var value in Feed.xmlElements)
		{
			if (Feed.adultTerms.Contains(value))
				return true;
		}
		return false;
	}
}

(Ignore the fact that an array and Array.Contains() is used for/on adultTerms instead of a Dictionary, and that “value” isn’t ToLower()ed. I’m trying to illustrate a point here, nitpicker!)

Only IsAdultCheck() needs access to the value of adultTerms, but in C#, the whole class can have access to that variable. Hiding it from the rest of the class, within the method, removes noise from the class. When examining a class you’re not familiar with for the first time, the less glutter, the better.

Not convinced? What if IsAdultCheck() is just the beginning—there are other methods that also have their own groups of static fields?

Would you rather see this, if you had to work with the Feed class:

public class Feed
{
	#region These could all be static locals!

	// Used by IsAdultCheck()

	private static readonly string[] adultTerms;

	// Used by NextPaginated()

	private static int pageCurrent;
	private static int pageStart;
	private static int pageEnd;
	private static string paginatedFeedUrl;

	// Used by Whatever()

	public static bool whateverSetting;
	public static string whateverSetting2;
	public static DateTime whateverSetting3;

	#endregion

	public static string NextPaginated(Feed feed)
	{ /* ... */ }
	
	public static string IsAdultCheck(Feed feed)
	{ /* ... */ }

	public static void Whatever()
	{ /* ... */ }
}

Or would you rather have all these private static variables encapsulated in each method in which they’re exclusively used, as local static variables?

Like I said, VB got it right when static local variables were included, starting with VB 3.0. adultTerms can be encapsulated in IsAdultCheck():

	
Public Class Feed
	Public Shared Function IsAdultCheck(ByRef feed as Feed) as Boolean
		Static adultTerms() as String = ...
		
		For Each term as String in adultTerms
			If term.Contains(term) Then Return True
			Return False
		Next
	End Function
End Class

What’s the argument against including static local variables in C#? I understand the need to differentiate the two .NET languages, but c’mon, I know a good thing when I see it! In

Update: I emailed the C# Team Project Manager, Mads Torgenson, to find out why the decision was made not to have static local variables. I’ll paraphrase his response: “There’s no need. Create a new class and encapsulate the method(s) and the static variable(s) in there.” In general I agree that this is a good idea, but then I wouldn’t have anything to complain about.

Full Support for Variable Declaration in Statements

Here’s some valid C#:

foreach(var url in urls)
	// ...
for(int i = 0; i < length; i++)
	// ...
&#91;/sourcecode&#93;

&#91;sourcecode language="csharp"&#93;
using(var buffer = new MemoryStream())
	// ...
&#91;/sourcecode&#93;

Notice how all three "for," "foreach," and "using" statements allow the declaration of a variable in them? They must be special, for some odd reason. Because here's some C# that's a no-go:

&#91;sourcecode language="csharp"&#93;
if ((var metaKeywords = AppSettings&#91;"MetaKeywords"&#93;) != null)
	Page.SetMetaTags(MetaTags.Keywords, metaKeywords);
&#91;/sourcecode&#93;

What's good about the for loops and the using statement above is that since the variables declared in the statements are only going to be used within the scope of the statements, they're inaccessible to the rest of the method in which the statements resides. This is another example of encapuslation that reduces extraneous noise, much like when I wrote of method-local static variables above. If the rest of the method doesn't need to use the variables, <i>they shouldn't be able to</i>.

Just as these snippets wouldn't be advisable if "i" and "buffer" were only used within the scope of the statements:


var i = 0;
for(; i < length; i++)
	// ...
&#91;/sourcecode&#93;

&#91;sourcecode language="csharp"&#93;
var buffer = new MemoryStream();
using(buffer)
	// ...
&#91;/sourcecode&#93;

And just as this wouldn't make sense&mdash;and isn't even possible:

&#91;sourcecode language="csharp"&#93;
string url;
foreach(url in urls)
	// ...
&#91;/sourcecode&#93;

Neither does it make sense to do the following if "metaKeywords" isn't going to be used outside the scope of the if statement (which you currently <i>have</i> to do in C#):


var metaKeywords = AppSettings["MetaKeywords"];

if (metaKeywords != null)
	Page.SetMetaTags(MetaTags.Keywords, metaKeywords);

The reason an inline variable declaration in an if statement is probably illegal, as someone pointed out to me, is that a lot of C and C++ code is terse—like a competition for fewest lines. The downside is readability and maintainability, which go hand-in-hand. Sure, it’s cool to refactor 10 lines into two, but it’s usually not so cool when a different developer goes in and tries to debug or modify that code. But if the readability of a for statement, for example, isn’t an issue in C#, then it seems rather arbitrary to me that if statements would be.

Sure, there’s room for abuse, but I don’t need C# to save me from myself. Perhaps C# needs to save itself from developers, such as those from dynamic or scripting languages, where this practice is more common, and clean code is sparser.

Non-integral Enums

Ever find yourself switch()ing an enum, and assigning different values to an object based on the case of that enum? For example, say we’ve got this enumeration:

enum AmericanHoliday
{
	NewYearDay,
	IndependenceDay,   
	Christmas
}

And depending on the AmericanHoliday selection, we have a switch that returns a corresponding date:

private DateTime GetHolidayDate(AmericanHoliday holiday)
{
	switch (holiday)
	{
		case AmericanHoliday.NewYearDay:
			return DateTime.Parse("1/1");
		case AmericanHoliday.IndependenceDay:
			return DateTime.Parse("7/4");
		case AmericanHoliday.Christmas:
			return DateTime.Parse("12/25");
		default:
			throw new NotImplementedException();
	}
}

We have this switch because AmericanHoliday enumerations are can only be byte, sbyte, short, ushort, int, uint, long, or ulong. The problem with integral-only is that a switch (or if) statement has to be used if you want that enum to represent anything other than an integral type (i.e. a number).

But what if we could use different types, that didn’t have to be constants, in an enum, like a DateTime?

enum AmericanHoliday
{
	NewYearDay = DateTime.Parse("1/1"),
	IndependenceDay = DateTime.Parse("7/4"),
	Christmas = DateTime.Parse("12/25")
}

We could eliminate the conversion from an enum to a DateTime performed in GetHolidayDate(), and simply write:

DateTime holidayDate = AmericanHoliday.NewYearDay; 

Or how about we had an enum that represented an error:

enum AccessDeniedReason
{
	UnknownUser = "The specified user is unknown.",
	InvalidPassword = "The specified password was invalid.",
	AccountDeactivated = "The account has been deactivated."
}

There are ways of achieving something similar in C#, but none of the workarounds are very good. Perhaps implementing such a thing just isn’t practical in a statically-typed language.

kick it on DotNetKicks.com

Comments (6)

Returning null from a class constructor?!

Just as an exercise, I thought I’d examine common creation patterns in C# and how operator overloading can expand these.

Typically, if the parameters passed into a class constructor are not valid and thus cannot create an instance of itself, an Exception is thrown. ArgumentNullException is most common when a parameter is null and shouldn’t be. Also common are ArgumentExceptions and FormatExceptions, especially when a constructor takes in a string that must abide by a syntax and doesn’t.

public class Expression
{
	public Expression(string pattern)
	{
		if (Expression.IsInvalidPattern(pattern))
		{
			throw new ArgumentException("pattern is invalid");
		}
		
		// Do something
	}
}

Then a try/catch block is used to catch Exceptions when instantiating the type:

Expression expression;
try
{
	expression = new Expression(pattern);
}
catch(ArgumentException)
{
	// Abort
}

An alternative would be to simply indicate that the instantiated class is in an invalid state:

public class Expression
{
	public readonly bool IsInvalid = false;
	
	public Expression(string pattern)
	{
		if (Expression.IsInvalidPattern(pattern))
		{
			this.IsInvalid = true;
			return;
		}
		// Do something
	}
}

The invalid state is readonly so that it can only be set by the constructor, and is then checked for before performing any operations with the new Expression:

Expression expression = new Expression(pattern);

if (expression.IsInvalid)
{
	// Abort
}

The Parse/TryParse pattern is also common. Instead of an Exception, readonly field, or get-only property, a static TryParse method is used that returns true or false, depending on whether the type can be created:

public class Expression
{
	public static bool TryParse(string pattern,
		out Expression expression)
	{
		if (Expression.IsInvalidPattern(pattern))
		{
			expression = null;
			return false;
		}
		
		expression = new Expression(pattern);
		
		return true;
	}
}

TryParse() is then used as in this example:

Expression expression;

if (!Expression.TryParse(pattern, out expression))
{
	// Abort
}

Another pattern which I haven’t seen used allows an invalid object to emulate a null reference. There are better solutions than this, which is why it isn’t used, but by using operator overloading, it can be accomplished nonetheless.

public abstract class Errorable
{
	protected bool isError = false;

	public static bool operator ==(Errorable left, object right)
	{
		if (right == null)
		{
			return left.isError;
		}
		
		return (left.GetHashCode() == right.GetHashCode());
	}

	public static bool operator !=(Errorable left, object right)
	{
		if (right == null)
		{
			return !left.isError;
		}
		
		return (left.GetHashCode() != right.GetHashCode());
	}
} 

public class Expression : Errorable
{
	public Expression(string pattern)
	{
		if (Expression.IsInvalidPattern(pattern))
		{
			base.isError = true;
			return;
		}
		// Do something
    }
} 

An Expression can then be evaluated to test for null, although we know that “expression” really isn’t a null referenced, which is one reason why this pattern is probably a bad idea.

Expression expression = new Expression(pattern);

if (expression == null)
{
	// Abort
}

Lastly, overloading the ! operator allows us to evaluate an object as fasly, which other languages (e.g. JavaScript) do by default:

public abstract class Errorable
{
	protected bool isError = false;

	public static bool operator !(Errorable errorable)
	{
		if (errorable == null || errorable.isError);
	}
}

public class Expression : Errorable
{
	public Expression(string pattern)
	{
		if (Expression.IsInvalidPattern(pattern))
		{
			base.isError = true;
			return;
		}
		// Do something
    }
}

To test an Expression as invalid, the ! operator is used:

Expression expression = new Expression(pattern);

if (!expression)
{
	// Abort
}

The first two patterns are common, and probably why the last two aren’t used. Generally, if there’s already a good, well-established pattern, there’s no reason to try something else. The alternative shown creation patterns were simply an exercise in operator overloading.

kick it on DotNetKicks.com

Comments (4)

Setting Meta Tags in ASP.NET with C# 3.0 Extensions

kick it on DotNetKicks.com

Here’s an extension on the System.Web.UI.Page class (the class for all “.aspx” files). The idea is to have keys in the Web.config appSettings that are the web site’s meta description and keyword tags, and they are set by calling this.SetMetaTags() on Page_Load() of your Page class.

using System;
using System.Web.UI;
using System.Web.UI.HtmlControls;

public partial class _Default: Page
{
	protected void Page_Load(object sender, EventArgs e)
	{
		// Set meta header tags; SetMetaTags throws
		// ArgumentNullException
		this.SetMetaTags(
			Website.AppSettings["DefaultMetaDescription"],
			Website.AppSettings["DefaultMetaKeywords"]);

	}
}

// This is actually in App_Code/Extensions, and PageExtensions is
// not within a namespace

public static class PageExtensions
{
	/// <summary>
	/// Sets the HTML meta description and keyword tags of a
	/// <c>Page</c>.
	/// </summary>
	/// <exception cref="ArgumentNullException">
	/// If description or keywords are null </exception>
	/// <exception cref="InvalidOperationException">
	/// If page.Header is null</exception>
	public static void SetMetaTags(this Page page, string description,
		string keywords)
	{
		if (description == null)
			throw new ArgumentNullException("description");

		if (keywords == null)
			throw new ArgumentNullException("keywords");

		HtmlHead header;

		if ((header = page.Header) == null)
				throw new InvalidOperationException
				("The page's markup must have runat=server in its <head>");

		ControlCollection headerControls = header.Controls;

		// New C# 3.0 feature: object initializers
		headerControls.Add(new HtmlMeta
		{
				Content = description,
				HttpEquiv = "description"
		});
		headerControls.Add(new HtmlMeta
		{
				Content = keywords,
				HttpEquiv = "keywords"
		});
	}
}

Comments (4)

Pagination widget/control

kick it on DotNetKicks.com

Want a JavaScript that draws a paging control into a <div>? It’s not unique in appearance, but there are no frameworks (e.g. Prototype, JQuery) requried, it’s relatively small (2.49 kb) before being compressed (1.2 kb after Packering), it’s cross-browser, and canby stylized using CSS. Here’s how she looks, stock:

PagerControl

Here’s the source and a small test page so you can figure out how it behaves. Create an “.htm” file, paste in this code, then enjoy:

<html>
<head>
	<style type="text/css">
		.spanPagerControlUnselected, .spanPagerControlSelected
		{
			font-family: arial;
			font-size: 9pt;
			font-weight: bold;
			border: 1px solid black;
			margin-right: 2px;
			margin-left: 2px;
			padding-left: 8px;
			padding-right: 8px;
			padding-top: 4px;
			padding-bottom: 4px;
			text-decoration:none;
			color:black;
		}

		.spanPagerControlSelected:hover
		{
			background-color:gray;
		}

		.spanPagerControlSelected, .spanPagerControlUnselected:hover
		{
			background-color:#1e4490;
			color:white;
		}

		.hellip
		{
			font-weight: normal;
			font-size: 8pt;
		}
	</style>
</head>
<body>
	<div id="divPager"></div>
</body>
</html>

<script type="text/javascript">

function PagerControl(parameters) {

	var PagerControl = new Object();

	// Public 

	PagerControl.page = function(pageNumber) {
		parameters.pagedCallback(pageNumber);
		this.currentPage = pageNumber;
		this.draw();
	};

	// Private

	PagerControl.currentPage = parameters.startPage;
	PagerControl.pageCount = parameters.pageCount;

	PagerControl.hasPreviousPage = function() {
		return (this.currentPage > 1);
	};

	PagerControl.hasNextPage = function() {
		return (this.currentPage < this.pageCount);
	};

	PagerControl.draw = function() {

		var createElement = function(page, selected, text) {	

			var elementNode = document.createElement(selected ? "span" : "a");

			if (!selected)
				elementNode.setAttribute("href", "javascript:" + parameters.varName + ".page(" + page + ")");

			elementNode.setAttribute("class", selected ? parameters.selectedCssClass : parameters.unselectedCssClass);
			elementNode.appendChild(document.createTextNode(text ? text : page));

			targetDiv.appendChild(elementNode);

		}

		var currentPage = this.currentPage;
		var pageCount = this.pageCount;
		var pagesToShow = parameters.pagesToShow;
		var hellipHTML = "<span class=\"" + parameters.hellipCssClass + "\">&hellip;</span>";

		// Reset the pager element; then paint it progressively

		var targetDiv = document.getElementById(parameters.targetDiv);
		targetDiv.innerHTML = "";

		var radius = Math.floor(parameters.pagesToShow/2);
		var start = currentPage - radius;
		var stop = currentPage + radius;

		if (start < 1) {
			start = 1;
			stop = (pagesToShow > pageCount)? pageCount : pagesToShow;
		}

		if (stop > pageCount) {
			stop = pageCount;
			var potentialStart = stop - pagesToShow;
			start = (potentialStart < 1)? 1 : potentialStart;
		}

		if (this.hasPreviousPage())
			createElement(parseInt(currentPage-1), false, parameters.previousPageLabel);

		if (parameters.alwaysShowFirst && start > 1) {
			createElement(1, false);
			if (start > 2)
				targetDiv.innerHTML += hellipHTML;
		}

		for (var i = start; i <= stop; i++)
			createElement(i, (i == currentPage));

		if (parameters.alwaysShowLast && stop < pageCount) {
			if (stop < pageCount - 1)
				targetDiv.innerHTML += hellipHTML;
			createElement(pageCount, false);
		}

		if (this.hasNextPage())
			createElement(parseInt(currentPage+1), false, parameters.nextPageLabel);

		// IE won't display class styles until this:
		targetDiv.innerHTML += "";
	};

	PagerControl.draw();

	return PagerControl;
}

</script>

<script type="text/javascript">

function loadPage(page) {
	// Ssend an XMLHttpRequest off to display the new page's contents in the
	// HTML document.
}

var pager = new PagerControl({

	startPage:1,
	pageCount: 100,
	pagesToShow: 7,
	unselectedCssClass: "spanPagerControlUnselected",
	selectedCssClass: "spanPagerControlSelected",
	hellipCssClass: "hellip",
	previousPageLabel: "previous",
	nextPageLabel: "next",
	alwaysShowFirst: true,
	alwaysShowLast: true,
	varName: "pager",
	targetDiv: "divPager",
	pagedCallback: loadPage

});

/*

Key for the parameters:

startPage (number) - The initial page that is selected.
pageCount (number) - The number of pages that exist.
pagesToShow (number) - The number of pages to show to the
	left and right of the current page for example, if
	startPage (above) is 10 and pagesToShow is 7, the
	buttons for pages 7 - 13 will be displayed.
unselectedCssClass (string) - The CSS class name for the
	page buttons that aren't the current page.
selectedCssClass (string) - The CSS class name for the
	page button that is curreltly selected.
hellipCssClass (string) - The CSS class name for <span>
	that displays "..." between the first and last page
	buttons if alwaysShowFirst and/or alwaysShowLast
	(below) are set to true.
previousPageLabel (string) - The text that appears in the
	page button for the previous page.
nextPageLabel (string) - The text that appears in the
	page button for the next page.
alwaysShowFirst (boolean) - Specify true value to always
	show a button for page 1 (and an ellipsis is
	displayed between page 1 and the curruent page minus
	half of the pagesToShow (above).
alwaysShowLast (boolean) -  Specify true value to always
	show a button for page 1 (and an ellipsis is displayed
	between the last page and the curruent page minus half
	of the pagesToShow (above).
varName (string) - The variable name of this new
	PageControl object.
targetDiv (string) - Where to draw the pager.
pagedCallback (function) - The function to fire when a
	surfer clicks a page.

*/

</script>

Comments (5)

JavaScript Query String

kick it on DotNetKicks.com

In the process of converting almost all the C# in our ASP.NET webpage codebehinds over to client-side JavaScript, we lost a useful member of the ASP.NET Page: its Request.QueryString. Take a look at this URL:

http://xml.piggy.com/default.aspx?q=software&page=10

The query string is everything that appears after the ‘?’ delimiter. As one can plainly see, it’s a simple collection of name or key/value pairs, each separated by an ‘&’. The key comes first, then a ‘=’, then the value. One would think that as prevalent as the use of query strings is for passing parameters to web servers to change the returned HTML document, there would be an object in JavaScript that served as a collection of these pairs hung off of window.location, where the URL-related properties lives.

The property window.location.search will return the query string portion of the URL, including the ‘?’ delimiter. For example, it would return “?q=software&page=10” in the example URL at piggy.com above (if there’s no query string, search is an zero-length string). The problem with search is that the key/value pairs have to be manually parsed out. A collection-like object accessible in the same manor as a hash table (e.g. var value = collection[key]) is a much more convenient and useful way of representing the query string.

In lieu of such an object in window, I’ve created a very simple JavaScript snippet that represents the query string as an object. The object’s properties are the query string keys, and the properties’ values are the key values. Make your life easier and drop this puppy somewhere in your global scope. A closure is used to make it play nice with the neighbors.

location.querystring = (function() {

    // The return is a collection of key/value pairs

    var queryStringDictionary = {};

    // Gets the query string, starts with '?'

    var querystring = decodeURI(location.search);

    // document.location.search is empty if no query string

    if (!querystring) {
        return {};
    }

    // Remove the '?' via substring(1)

    querystring = querystring.substring(1);

    // '&' seperates key/value pairs

    var pairs = querystring.split("&");

    // Load the key/values of the return collection

    for (var i = 0; i < pairs.length; i++) {
        var keyValuePair = pairs&#91;i&#93;.split("=");
        queryStringDictionary&#91;keyValuePair&#91;0&#93;&#93; 
                = keyValuePair&#91;1&#93;;
    }

    // toString() returns the key/value pairs concatenated

    queryStringDictionary.toString = function() {

        if (queryStringDictionary.length == 0) {
            return "";
        }

        var toString = "?";

        for (var key in queryStringDictionary) {
            toString += key + "=" + 
                queryStringDictionary&#91;key&#93;;
        }

        return toString;
    };

    // Return the key/value dictionary

    return queryStringDictionary;
})();
&#91;/sourcecode&#93;<p>Getting the value for the "q" key in the piggy.com URL now is as simple as:</p>
// If "q" isn't there, val is undefined
var val = location.querystring["q"];

And the collection can easily be enumerated too:

        for (var key in location.querystring) {
            alert(key + "=" + location.querystring[key]);
        }

Lastly, the entire query string can be retrieved (includes the leading ‘?’) via toString():

        alert(window.location.querystring.toString());

Comments (11)

Local XMLHttpRequest Debugging

kick it on DotNetKicks.com

Disconnected XMLHttpRequest Debugging

A recent project has me working closely with the browser XMLHttpRequest object. Like any good little dev., I’ve been writing tests for various Ajax routines. These test are simple .htm files run from a local directory on my computer—they do not reside on an HTTP server. Consequently, after an XMLHttpRequest has its send() fired, no actual HTTP request is made. Instead, the browser simply accesses the file on the harddrive via I/O operations—not the HTTP protocol. During my testing experience with a local setup, I’ve noticed a few quirks that deserve attention. If you’re a developer also debugging in a disconnected environment, you may find these notes useful.

Just a note when I get started: when I refer to “Mozilla”, I’m referring to Mozilla 5, and cannot speak for other version.

As another aside, shouldn’t “XMLHttpRequest” have been called simply “HttpRequest”? Think outside the (XML) box, people.

Cross-browser new XMLHttpRequest()

Now, with that out of the way, as many already known, XMLHttpRequest has to be accessed through ActiveX in IE. There are more than a few different versions of XMLHttpRequest. To instantiate a new XMLHttpRequest without having to worry about whether the client browser is IE or other, I was using the snippet below in the global (window) scope:

if (typeof(XMLHttpRequest) == "undefined") {
	function XMLHttpRequest() {
		try { return new ActiveXObject("MSXML3.XMLHTTP") }catch(e){}
		try { return new ActiveXObject("MSXML2.XMLHTTP.3.0") }catch(e){}
		try { return new ActiveXObject("Msxml2.XMLHTTP") }catch(e){}
		try { return new ActiveXObject("Microsoft.XMLHTTP") }catch(e){}
		return null;
	};
}
// var httpRequest = new XMLHttpRequest();

One can see that if the browser is IE, the latest commonly-distributed version is attempted to be used, then an older one, and down the line. An interesting thing happens when you try to call the open() an XMLHttpRequest to a local file or URL “#” with only MSXML3.XMLHTTP: it doesn’t work—an Error is thrown:

// Throws Error, just like if "asset.xml" was "#"
var httpRequest = new ActiveXObject("MSXML3.XMLHTTP");
httpRequest.open("GET", "asset.xml", true);

// Or any version from the code block above this one
var httpRequest = new ActiveXObject("MSXML2.DOMDocument");
// No Error, responseXML is loaded if asset.xml is valid XML
httpRequest.open("GET", "asset.xml", true);

// Mozilla
var httpRequest = new XMLHttpRequest();
// No Error, responseXML is loaded if asset.xml is valid XML
httpRequest.open("GET", "asset.xml", true);

Local files cannot be accessed using the MSXML3.XMLHTTP library. Don’t waste your time.

XMLHttpRequest.status

To determine if an XMLHttpRequest successfully retrieved a response, code should check for the “done” readyState, 4, and an HTTP response status code, status, of 200, meaning OK:

// Pre-defined constants are used here
// READYSTATE.DONE == 4
// STATUSCODE.OK == 200
if (httpRequest.readyState == READYSTATE.DONE
     && httpRequest.status == STATUSCODE.OK) {
	successCallback(httpResponse);
}

In both Mozilla and IE, if the HTTP request is to a local file, however, the status never changes from 0. The readyState of course does, however. Thus, the snippet above becomes:

// STATUSCODE.DEFAULT == 0
if (httpRequest.readyState == READYSTATE.DONE &&
   (httpRequest.status == STATUSCODE.DEFAULT ||
    httpRequest.status == STATUSCODE.OK)) {
	successCallback(httpRequest);
}

Response Headers

Try to get a specific header value such as “content-type” using getResponseHeader("content-type") in Mozilla and IE, and an empty string will be the result. Same goes for getAllResponseHeaders()—nothing but an empty string. That’s logical since HTTP isn’t actually used though, of course.

responseXML‘s Value

With Mozilla, whether an XMLHttpRequest grabs a local file or actually makes an HTTP request, if the data it processes is valid XML, the data will be loaded into a DOM object and made accessible in responseXML. If the file or response cannot be loaded into the DOM, responseXML is null:

// Mozilla example 
if (httpRequest.readyState == READYSTATE.DONE &&
   (httpRequest.status == STATUSCODE.DEFAULT ||
    httpRequest.status == STATUSCODE.OK)) {
	
		if (httpRequest.responseXML == null) {
			// Failed; didn't load data into DOM object
			return;
		}
		// Success
		successCallback(httpRequest.responseXML);
}

IE, on the other hand, creates an empty DOM object and places it in the responseXML regardless of what the file or response is. Then, if the response data is valid XML, it will be loaded into the responseXML DOM object. Checking if responseXML is null, therefore, will tell you nothing. What you want to check for is the DOM root. If responseXML.documentElement (the root element) is null, then the DOM object is empty—the XML data was not loaded into it:

// IE-only example
if (httpRequest.readyState == READYSTATE.DONE &&
   (httpRequest.status == STATUSCODE.DEFAULT ||
    httpRequest.status == STATUSCODE.OK)) {

		if (httpRequest.responseXML.documentElement == null) {
			// Failed; didn't load data into DOM object
			return;
		}
		// Success
		successCallback(httpRequest.responseXML);
}

With local files, IE, once again, behaves unbecomingly. If the XMLHttpRequest object grabs an “.xml” file or file with valid XML, the DOM object will always be empty. My guess is that the XMLHttpRequest checks for the “content-type” header and loads DOM only if the response is “text/xml”. And since there are no headers because no HTTP request is actually made for local files, XMLHttpRequest isn’t smart enough to load the responseXML object. If one is doing local testing and getting a bunk DOM object, simply load the responseText using ActiveX::

// Using...

if (typeof(XMLHttpRequest) == "undefined") {
	function XMLHttpRequest() {
		try { return new ActiveXObject("MSXML3.XMLHTTP") }catch(e){}
		try { return new ActiveXObject("MSXML2.XMLHTTP.3.0") }catch(e){}
		try { return new ActiveXObject("Msxml2.XMLHTTP") }catch(e){}
		try { return new ActiveXObject("Microsoft.XMLHTTP") }catch(e){}
		return null;
	};
}

// Cross-browser, handlers IE and Mozilla cases

httpRequest = new XMLHttpRequest();
httpRequest.open("GET", "assets.xml", true);

httpRequest.onreadystatechange = function() {
	var isLocal = (httpRequest.status == STATUSCODE.DEFAULT);
	
	if (httpRequest.readyState == READYSTATE.DONE &&
	   (httpRequest.status == STATUSCODE.OK || isLocal)) {
		
		if (httpRequest.responseXML == null)
			// Mozilla failure, local or HTTP
			failure();
		else if(httpRequest.resonseXML.document == null) {
			// IE 
			if (!isLocal)
				// HTTP failure
				failure();
			else {
				// Local failure--always happens, try
				// using Microsoft.XMLDOM to load
				var xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
				xmlDoc.async = false;
				xmlDoc.loadXML(httpRequest.responseText);
				
				if (xmlDoc.documentElement != null)
					success(xmlDoc);
				else
					failure();
			}
		}
		else
			success(httpRequest.responseXML);
	}
};

Comments (4)

JSON vs. JavaScript

kick it on DotNetKicks.com

JSON is not JavaScript (nor a JavaScript “subset”)

JSON is not only a syndication format (e.g. an alternative to XML), it is a language-independent data interchange format.

As such, while JSON objects are based on the JavaScript object literal, they are not the same. In fact, JSON objects can be invalid as JavaScript objects. Therefore, JSON should be transformed to JavaScript-compliant JSON before feeding Ajax or JavaScript applications.

This flexibility on the part of JSON increases its extensibility as an interchange format. Although JSON makes a strong competitor to XML for Ajax applications, it is meant to be suitable for other mediums as well (hence JSON RPC).

How they are Different—Property Names

Their difference is in what is permissible for property names, defined by the ECMA Standard for JavaScript, and JSON member (property) names, defined by RFC 4627.

JavaScript requires property names to start with an underscore (_), letter, or dollar sign ($), and contain only alphanumeric characters, underscores, and dollar signs:

var chris = {
	lastName: "O'Brien",
	_city: "Seattle",
	income$: "Dollar sign"
};
alert("Chris lives in " + chris._city);

JSON property names, on the other hand, may contain any Unicode characters, provided proper escaping of control characters. For example, the object defined in by the { } literal below is valid JSON, yet cannot be used to create an object in JavaScript.

// throws an Error
var impossible = {
	"411": "yohoho.com",
	"xmlns:x": "Invalid character",
	"Company name": "Spaces",
	"": "Zero-length string"
};

Because JSON property names can contain spaces and JavaSript’s cannot, JSON property names must be encapsulated in quotations. In JavaScript, encapsulating property names is optional. Quotations also make an empty zero- or variable-length property name possible in JSON.

Reserved Words

Furthermore, JavaScript (like all well-adopted languages) has reserved words. These are words that have special meaning and cannot be used as property names. Below is a table of reserved words. Words without asterisks are reserved keywords and words with asterisks are words reserved for future use.These can be found in ECMA-262 under heading 7.5.

abstract*
boolean*
break
byte*
case
catch
char*
class*
const*
continue
debugger*
default
delete
do
double*
class*
const*
continue
debugger*
default
delete
do
double*
else
enum*
export*
extends*
final*
finally
float*
for
function
goto*
if
implements*
import*
in
instanceof
int*
interface*
long*
native*
new
package*
private*
protected*
public*
return
short*
static*
switch
super*
synchronized*
this
throw
throws*
transient*
try
typeof
volatile*

Ordered Steps for Transforming JSON for JavaScript

To guarantee that JSON data can be consumed by JavaScript, like so:

var obj = eval("(" + jsonString + ")");

…the operations below need to be performed on a string of JSON data. This will ensure that the JSON data is JavaScript-compliant.

  1. Remove or replace any spaces in property names.
  2. Remove or replace the first character in the name of a property if it is not an underscore, letter, or dollar sign.
  3. Remove or replace any non-alphanumeric character, save for underscores and dollar signs.
  4. Replace property names that are reserved words in JavaScript.
  5. If a property name is an empty, zero-length string, replace it with a valid JavaScript property name.
  6. Optionally, quotations around property names may be removed.

† Quotations and control characters must be escaped. Check out the RFC for details.

Comments (9)

« Newer Posts · Older Posts »