Closing and Re-opening tabs in Visual Studio with Ctrl+W

Visual Studio 2017 Undo-Close Update: The Productivity Power Tools have spun off into a bunch of more-focused extensions. To get Undo Close in Visual Studio 2017, you will want the Power Commands extension now.

Visual Studio 2013 Undo-Close Update: Since the prior options for re-opening closed tabs fell apart with the release of Visual Studio 2013, you will need the newly released Productivity Power Tools 2013.

Update: now with the ability to re-open closed tabs with Ctrl+Shift+T. This also allows you to re-open tabs closed by a project file reload, which is fantastic!

Ever tried to close a tab in Visual Studio 2010/2012 with Ctrl+W. If so, you find yourself selecting the current word in your text editor (Edit.SelectCurrentWord). I don’t use that shortcut, though I could see it being handy over my usual Ctrl+Shift+Right-/Left-Arrow. I do, however, use Ctrl+W to close windows/tabs in just about every other program I use. In order to make that shortcut work for your Visual Studio editing, you just need to assign it to File.Close instead.

For the visual, here’s a snapshot similar to what you will want (note: I already made this change before snapping a pic, so yours may look slightly different).

Visual Studio Keyboard Options

  1. Go into the Tools->Options menu.
  2. Select Environment->Keyboard in that window.
  3. Type “File.Close” in the “Show commands containing” field and select it in the list when it shows.
  4. Choose “Text Editor” for the “Use new shortcut in” field.
  5. In “Press shortcut keys” field, press Ctrl+W.
  6. Click “Assign” to make it happen.
  7. Close the Options window with the “OK” button.

Now, you can happily close windows with Ctrl+W in Visual Studio.

Next step

Figure out how to get Ctrl+Shift+T to bring back the last closed window. If I find a way, I’ll post it.

Update (2012-02-16): Re-open previously closed tabs (with PowerCommands for Visual Studio 2010 [also works for Visual Studio 2012])

As Sergey Vlasov pointed out in a comment, the PowerCommands for Visual Studio 2010 adds the ability to “Undo Close” on previously closed tabs. Since it adds them as commands to Visual Studio, you can also override the default keyboard shortcut (Ctrl+Shift+Z) to a more web-browser-esque shortcut: Ctrl+Shift+T. If you already had this installed for Visual Studio 2010 when you started using Visual Studio 2012, just install the PowerCommands for Visual Studio 2010 again and it will offer to add it to Visual Studio 2012. You will still need to make the shortcut assignment in 2012.

Visual Studio Keyboard Options (Edit.UndoClose)

Just like with your favorite web browser, this will cycle through previously closed tabs, resurrecting them from their graves and restoring your cursor location to right where you left off when you closed them. Amazingly enough, this also works when Visual Studio closes your active windows when a project reloads.

Initial setup for a new ASP.NET MVC site in IIS7

Background

Over the years, I have spent far too many hours running the same set of commands against ASP.NET and ASP.NET MVC sites to bring them up to what I consider a starting point. Most of the time, I have to refresh myself about how to do at least one of them. This is a list of those commands in a central location for myself and anyone else to use. As with any good instructions, there is no guarantee you won’t completely destroy your server, site, or soul using these commands. I can only say they worked for me once.

  1. Remove unnecessary HTTP headers
    1. Server
    2. X-Powered-By
    3. X-AspNet-Version
    4. X-AspNetMvc-Version
  2. Adding dynamic content compression (e.g., GZIP)
  3. GZIP for JSON and JSONP content types (e.g., “application/json”)
  4. Complete appcmd command-line list

Remove unnecessary HTTP headers

By default, IIS and ASP.NET give you a few headers in each and every HTTP response sent out.

Server: Microsoft-IIS/7.5 (or 7.0, or whatever IIS version you have)
X-Powered-By: ASP.NET
X-AspNet-Version: 4.0.303319 (or 2.0.50727, or 1.1.4322, or whatever version you have)
X-AspNetMvc-Version: 3.0 (or 2.0, or 1.0, or whatever version you have)

People will rant and rave about removing these headers for security and whether that helps. I am firmly on the side of “not-why-I-care-at-all”. I always figure if I am a target, obscurity doesn’t buy me anything when someone with very little computing power can just throw packets exploiting all the major frameworks at me and see which stick (which happens even when you aren’t a real “target”). The reason I do care is that removing these headers strips out even more data that has to be sent between you and the end user. Even though the size difference is small, it applies to every single HTTP response handled by your site. For such simple one-time changes, I will gladly take the savings.

Server (the annoying one)

This HTTP header appears to be the most annoying to remove. In fact, short of an HTTP module, the common method for doing so is a new line in a [probably new] event handler in global.asax.cs (or global.asax.vb).

protected void Application_PreSendRequestHeaders() {
    HttpContext.Current.Response.Headers.Remove("Server");
}

It’s not as pretty as the rest of the removal stuff below, but it gets the job done. In fact, they could all be solved this way, but I tend to lean on IIS to do the heavy lifting when I can.

X-Powered-By (the more annoying web.config/IIS one)

Many places that offer suggestions on removing the X-Powered-By: ASP.NET header talk about using IIS. You can certainly do this if you have permission to do so on your hosting provider and like the IIS GUI. I prefer things that I can push out with source control, so I go the web.config route.

<system.webServer>
    <httpProtocol>
        <customHeaders>
            <remove name="X-Powered-By" />
        </customHeaders>
    </httpProtocol>
</system.webServer>

Command line happiness:

%windir%\System32\inetsrv\appcmd.exe set config "{your-site-name}" /section:system.webServer/httpProtocol /-"customHeaders.[name='X-Powered-By']"

The root source of this setting is actually found in your server’s applicationHost.config (%windir%\system32\inetsrv\config\applicationHost.config). If you have the ability to modify that file, you can remove it there to make it the default for all IIS sites.

<system.webServer>
    <httpProtocol>
        <customHeaders>
            <clear />
            <add name="X-Powered-By" value="ASP.NET" /> <!--Remove this line.-->
        </customHeaders>
        <redirectHeaders>
            <clear />
        </redirectHeaders>
    </httpProtocol>
</system.webServer>

Command line happiness:

%windir%\System32\inetsrv\appcmd.exe set config -section:system.webServer/httpProtocol /-"customHeaders.[name='X-Powered-By']" /commit:apphost

X-AspNet-Version (the easy web.config one)

To make X-AspNet-Version: {your-aspnet-version} go away, you just need to tweak your site’s web.config file. In the <system.web> section, under <httpRuntime>, which is probably already there, just make sure the enableVersionHeader="false" attribute is added to it.

<system.web>
    <httpRuntime enableVersionHeader="false" />
</system.web>

Command line happiness:

%windir%\System32\inetsrv\appcmd.exe set config "{your-site-name}" /section:system.web/httpRuntime /enableVersionHeader:false

I’m guessing this could be done at the applicationHost.config level, though I have never tried it (no real excuse, just too scared to mess with such a vital config entry too much).

X-AspNetMvc-Version (the global.asax code one)

Obviously, if you are tweaking a non-MVC ASP.NET site, you can ignore this one.

To make X-AspNetMvc-Version: {your-aspnetmvc-version} go away, you need to add some code and redeploy your site. In your global.asax.cs (or global.asax.vb) file, you will need to add a single line to the application start event.

protected void Application_Start() {
    MvcHandler.DisableMvcResponseHeader = true;
    // ...
}

Adding dynamic content compression (e.g., GZIP)

Reducing the amount of data you send over the wire is pretty good idea, especially if you are targeting low-bandwidth users like mobile browsers. While GZIP has some performance impact on the server and client, it is negligible on most systems. To add this system to IIS, add in the “Dynamic Content Compression” role service under the “Web Server (IIS)” role in “Server Manager”. If I am doing it visually, I typically just follow along with this iis.net article on the process.

Command line happiness:

%windir%\System32\inetsrv\appcmd.exe set config -section:system.webServer/globalModules /+[name='DynamicCompressionModule',image='%windir%\System32\inetsrv\compdyn.dll'] /commit:apphost
%windir%\System32\inetsrv\appcmd.exe set config -section:system.webServer/modules /+[name='DynamicCompressionModule',lockItem='true'] /commit:apphost

If this has already been installed, this will error out with a message about adding a duplication “DynamicCompressionModule” entry.

GZIP for JSON and JSONP content types (e.g., “application/json”)

[Update 2012-02-02] NOTE: If you have a load-balancer, you may need to figure out how to get it to allow these content types through as GZIP. In one situation where I added this setting to a series of servers behind F5’s BIG-IP load balancer, it still wouldn’t get GZIP out to the requester. BIG-IP was either stripping the request of Accept-Encoding: gzip or getting GZIP from IIS and decompressing it before letting it return to the original requester (I didn’t confirm what was happening, but I would assume the former since it is less CPU-intensive).

While getting GZIP compression is great for HTML, JavaScript, CSS, and most others with “Dynamic Content Compression” is great, it drops the ball on content types it doesn’t have explicit orders to manipulate. One such content type that should get compression is dynamically-generated JSON and JSONP.

While working on the [Sierra Trading Post API](https://dev.sierratradingpost.com/] I noticed that IIS with dynamic compression enabled doesn’t do anything to JSON content types, “application/json” (JSON) and “application/javascript” (JSONP). Compression is decided by the content types listed in applicationHost.config (%windir%\system32\inetsrv\config\applicationHost.config). By default, you get most of the types served by the average ASP.NET site: text/* (covering text/html). If you need to add more to this list, add them to the config file. It does not matter if they come before or after the <add mimeType="*/*" enabled="false" /> wildcard entry that is there by default.

<system.webServer>
    <httpCompression ... >
        ...
        <dynamicTypes>
            ...
            <add mimeType="application/json; charset=utf-8" enabled="true" />
            <add mimeType="application/javascript; charset=utf-8" enabled="true" />
        </dynamicTypes>
    </httpCompression>
</system.webServer>

Command line happiness:

%windir%\System32\inetsrv\appcmd.exe set config -section:system.webServer/httpCompression /+"dynamicTypes.[mimeType='application/javascript; charset=utf-8',enabled='True']" /commit:apphost
%windir%\System32\inetsrv\appcmd.exe set config -section:system.webServer/httpCompression /+"dynamicTypes.[mimeType='application/json; charset=utf-8',enabled='True']" /commit:apphost

For some reason, these are not recognized without the charset=utf-8 part. As well, if you don’t notice a change right away, that’s normal; you must recycle the app pool first (TechNet has a nice article with info about app pool recycling and why it doesn’t happen here).

 %windir%\System32\inetsrv\appcmd recycle apppool /apppool.name:"{your-app-pool-name}"

Again, some people would suggest an HTTP module for handling this, but it seems like IIS would be better equiped to handle this since it is already doing so for the rest of my outbound content.

Complete appcmd command-line happiness

If you are following along at home, most of this stuff can be poked into server config files, either applicationHost.config or your site’s web.config (all but the Server: Microsoft-IIS/7.5 HTTP header). If you want the fire-and-forget solution, here are all the commands smashed together for your batch file use.

%windir%\System32\inetsrv\appcmd.exe set config "{your-site-name}" /section:system.webServer/httpProtocol /-"customHeaders.[name='X-Powered-By']"
%windir%\System32\inetsrv\appcmd.exe set config "{your-site-name}" /section:system.web/httpRuntime /enableVersionHeader:false
%windir%\System32\inetsrv\appcmd.exe set config -section:system.webServer/globalModules /+[name='DynamicCompressionModule',image='%windir%\System32\inetsrv\compdyn.dll'] /commit:apphost
%windir%\System32\inetsrv\appcmd.exe set config -section:system.webServer/modules /+[name='DynamicCompressionModule',lockItem='true'] /commit:apphost
%windir%\System32\inetsrv\appcmd.exe set config -section:system.webServer/httpCompression /+"dynamicTypes.[mimeType='application/javascript; charset=utf-8',enabled='True']" /commit:apphost
%windir%\System32\inetsrv\appcmd.exe set config -section:system.webServer/httpCompression /+"dynamicTypes.[mimeType='application/json; charset=utf-8',enabled='True']" /commit:apphost
%windir%\System32\inetsrv\appcmd recycle apppool /apppool.name:"{your-app-pool-name}"

If you want to do all this to applicationHost.config alone, you just swap out the first two lines from hitting “{your-site-name}” to pointing to the apphost config. Here they are all smashed together for that variant. NOTE: I have not confirmed that setting enableVersionHeader in applicationHost.config will work.

%windir%\System32\inetsrv\appcmd.exe set config -section:system.webServer/httpProtocol /-"customHeaders.[name='X-Powered-By']" /commit:apphost
%windir%\System32\inetsrv\appcmd.exe set config /section:system.web/httpRuntime /enableVersionHeader:false /commit:apphost
%windir%\System32\inetsrv\appcmd.exe set config -section:system.webServer/globalModules /+[name='DynamicCompressionModule',image='%windir%\System32\inetsrv\compdyn.dll'] /commit:apphost
%windir%\System32\inetsrv\appcmd.exe set config -section:system.webServer/modules /+[name='DynamicCompressionModule',lockItem='true'] /commit:apphost
%windir%\System32\inetsrv\appcmd.exe set config -section:system.webServer/httpCompression /+"dynamicTypes.[mimeType='application/javascript; charset=utf-8',enabled='True']" /commit:apphost
%windir%\System32\inetsrv\appcmd.exe set config -section:system.webServer/httpCompression /+"dynamicTypes.[mimeType='application/json; charset=utf-8',enabled='True']" /commit:apphost
%windir%\System32\inetsrv\appcmd recycle apppool /apppool.name:"{your-app-pool-name}"

From jsFiddle to GitHub to AppHarbor to stackgeography.com

Update

I finally put together an app listing on StackApps. If you like stackgeography.com and you have a StackApps account, I’d love for your vote on the StackGeography app page.

Background

My latest project at my full-time job with Sierra Trading Post is their Mashery-managed API. It currently includes most everything you would expect from a retail API: departments, brands, products, search, and reviews. I would like it to include a few things that are less typical but fun to work with from a data aggregation standpoint. One example: recent order locations. While it may not see a lot of outside use, it would be quite nice for our internal observation; it would go great on the handful of large screens around the company with site statistics continually updating on them.

In order to put together a proof-of-concept that I could even do this, I wanted to make a mapping demo using someone else’s API. Since I am responsible for the Sierra Trading Post API, I try to research other APIs to learn from their successes and pain points. One such API that has provided a lot of [positive] learning is the Stack Exchange API. It just so happened that they were releasing version 2 of their API, so I decided to use that for my first proof-of-concept mapping fun. It didn’t hurt that they were tossing out a few prizes over on StackApps.com for good apps using the updated API.

I started with a few minutes of poking around the Google Maps API and quite a few more minutes poking around the 2.0 version of the Stack Exchange API. After that, I immediately ran to my favorite HTML/JavaScript/CSS prototyping grounds.

Stage 1: jsFiddle

It all started in jsFiddle and grew into a huge mess of nasty JavaScript. After exactly 337 revisions over a week, some to get the basics working and some to refactor the ugliness, I decided to put the code and markup somewhere more permanent. Along the way, I learned a few things.

  • When you poll an API regularly, you eat up your daily allocation of requests pretty quickly (300 per IP per day for the Stack Exchange API), even when you bump it back to every minute and have multiple tabs running simultaneously.
  • APIs never offer things exactly the way you want it. It certainly makes me sympathetic to the API users I cater to.
  • For any fairly complex JavaScript, you run out of room on a single screen with jsFiddle’s four iframes, Chrome’s JavaScript debugging window, and Chrome’s debugging console on a single screen.

Stage 2: GitHub

After exactly 337 revisions over a week through jsFiddle, I decided to put the code and markup somewhere more permanent. Enter GitHub and the StackGeography repository. I’ve played with Git about 20 minutes total, but I’d researched it for several hours and I have played with Mercurial enough to understand most of the core concepts. Since it was already installed and configured for another project, I was up and running in a few minutes.

I copied out the JavaScript and CSS into separate files, and threw the HTML into an HTML5 page referencing them both. From WebMatrix 2 Beta, I opened the folder as a new site. I was now working from a local version of my jsFiddle. Of course, my hosting options were limited. I have a tendency to “host” files from my DropBox’s public folder, especially files I reference in jsFiddle demos. I could also slap it up on one of my other domains, I even considered this one, but I wanted something more professional.

Stage 3: StackGeography.com + AppHarbor

After I ruled out hosting under an existing domain, I knew I wanted the project to have its own domain. I had already named the project “StackGeography” (formerly “stackviz”) on its StackApps registration, so I gave that a shot and found stackgeography.com available. I know everyone hates GoDaddy for a bunch of things (and I totally agree on the SOPA issue), but they are dirt cheap for domain registrations. Despite the terrible experience I had with their 4G hosting off this WordPress blog, I haven’t had any trouble with domain registrations or their name servers. After tip-toeing through the upsell landmines, I had the domain. Having a domain, though, didn’t get me hosting.

Somewhere in my past, I had read a few snippets on AppHarbor and how it could automagically publish your ASP.NET projects from a source control repository. Since I tend to hate all the overhead that goes into setting up those things manually, and I wanted to be quick on this one, I spent some time researching their system. It seemed simple enough, and it really is; you just configure the AppHarbor GitHub service hook. Sadly, it took me three blog posts about their system to figure out what they meant by “Application Slug”; it’s just the name of your AppHarbor application. I’ve heard the term “slug” used that way before, but it just didn’t click for me here.

Of course, I had to change up my project a little. Since AppHarbor expects a .NET project, I moved all my files to an new, empty ASP.NET application and committed the changes to GitHub. In a few minutes, I checked stackgeography.com and found my demo working fine, it was probably there in a few seconds since updates seemed to be almost instant. Amazing stuff.

Update 2012-02-03: AppHarbor was apparently in beta this whole time and I didn’t notice. They have since announced a pricing system, including charging for custom hostnames; you still get a free {yourappname}.apphb.com subdomain to use. Since stackgeography.com is not a revenue-generating site (nor do I plan to make it one), I will probably just use their free one and point stackgeography.com at it.

Check it out

While I can’t guarantee the app won’t blow through its API allocation of 10,000 requests per day (at 300 per IP), I would love to have you check out stackgeography.com and give me some feedback about it. Bugs, features, or otherwise, it’s out there for anyone to see it working or see how it works. I have plenty of plans on features to add, and I definitely want to improve the code quite a bit before I put it to use with the Sierra Trading Post API, but I think now is as good a time as any to get it out in the hands of others.

If you want to learn more about my plans for stackgeography.com, check out the StackGeography README.md file on GitHub.

Adapting Visual Studio code styling differences for open source project contribution

Background

Today, while incorporating Lee Dumond’s MVC Route/URL Generation Unit Tester into a project, I found a desire to contribute some code I thought would make the package easier to use. Unfortunately, the project code formatting looks nothing like my preferred conventions (some form of 1TBS, I guess). Until Visual Studio offers a way to distribute code style settings to source control consumers easily, I needed a different option.

While preparing demos for a mobile web development talk for the Cheyenne Computer Professionals group, I stumbled on Mike Minutillo’s tip for running a “demo” instance of Visual Studio where I could sandbox my presentation settings optimized for an elderly VGA projector. This sparked an workaround idea for dealing with multiple formatting settings of various projects I may work on.

Rather than force my conventions on the project (generally not acceptable) or give up on my own style (generally not acceptable), I decided to try using a “demo” instance of Visual Studio with that projects styling conventions set.

  1. Right-click where I want the shortcut.
  2. Specify a path to Visual Studio 2010 using the /RootSuffix option.
    1. On 64-bit Windows, %programfiles(x86)%\Microsoft Visual Studio 10.0\Common7\IDE\devenv.exe" /RootSuffix YourStylingNameHere
    2. On 32-bit Windows, %programfiles%\Microsoft Visual Studio 10.0\Common7\IDE\devenv.exe" /RootSuffix YourStylingNameHere
  3. Give the shortcut a name. I tend to go something like this: “Visual Studio (SomeProjectName)”.

Using this method, I can create a few common formatting variations. Then, whenever I want to work on a given project, I will just slap a new Visual Studio shortcut pointing to the appropriate suffix matching that projects formatting style.

I created an instance of Visual Studio that uses the default settings. Any new project I want to play with that uses those settings gets a custom VS shortcut that points to my “DefaultVsFormatting” root suffix.

By keeping the suffix names consolidated, all projects using those settings are present in that instance’s recent projects, both in the File menu and the Start Page (and its pinned projects).

Drawbacks

While it is great to be able to spawn up a Visual Studio instance, it does complicate some things. If I have multiple instances of Visual Studio running with different prefixes, I have no way of telling which is which without knowing what project I opened from what prefix instance.

It also presents a problem for someone who develops on multiple machines. I want the RootSuffixes I use to be identical on all machines. For the default settings, assuming I have Visual Studio in the same location on every machine, I could simply keep these %programfiles(x86)%-based shortcuts on a shared location like my Dropbox folder where they would work for all machines. If I have custom settings, though, I would need to keep copies of the settings folder data and registry settings.

Keeping the file system part consistent seems easy enough; I could just use a symbolic link to point Visual Studio to them in their shared location (change to your own path to settings folders).

mklink /d %LocalAppData%\Microsoft\VisualStudio\10.0DefaultVsFormatting %userprofile%\Dropbox\VisualStudioSettings\DefaultVsFormatting

There are also registry keys that are part of this process, too. Unfortunately, it appears that all the formatting settings are maintained there. This would take some extra work and make any changes difficult to propagate. After I create a suffix variant, I need to export the registry settings for the two registry keys (folders in the tree).

  1. Run ‘regedit’.
  2. Export the two prefix-based keys that are created (“10.0DefaultVsFOrmatting” and “10.0DefaultVsFOrmatting_Config”, in this case).
  3. Import those two keys on any other machines.

From there, any time I make a config change, I need to repeat this process on any machine that shares these settings. Not ideal, but it gets the job done.

UPDATE:

Unfortunately, after trying this, I found out many of the registry settings are set to explicit user-based paths. I need to try making those paths relative to various path variables (e.g., %userprofile%) before exporting the registry key and importing it elsewhere, but that may blow up Visual Studio. If you use the same user on all of your development machines, you are likely fine with this process; if not, you will need to tweak them accordingly.

Alternative (Visual Studio settings files)

Jon Galloway’s comment on Mike’s post has a valid point. It is definitely a useful option to just use a custom Visual Studio settings file (via Paul Stubbs) that can be imported as desired. I ended up using the /RootSuffix route for a couple reasons:

  • Every time I make a common change to the settings (admittedly rare), I would need to update all my various settings files.
  • Importing settings files would take more steps than launching a shortcut, at least unless there is some command-line option I haven’t learned yet where I can use to give Visual Studio the settings for that run.

No matter what method I use, it gets even more difficult to manage these situations when someone has a tool like JustCode or ReSharper installed on their machine. They would then need to maintain settings files for these tools as well, potentially needing to uninstall them for a given project to keep them from going all Rambo on the foreign code.

Conclusion

Ideally, any given Visual Studio-based project would just have a file shared among contributors that enforces the code styling conventions chosen for the project.

Until settings are a part of a project this solution works great on a small scale but is a bit difficult to scale to multiple machines. That said, I only have two machines on which I actively write code, so I will live with the hassle for now.

Getting dynamic ExpandoObject to serialize to JSON as expected

Serializing ExpandoObjects

I am currently creating a JSON API for a handful of upcoming Sierra Trading Post projects. When I found myself generating JSON for a stripped-down representation of a number of domain classes, all wrapped with some metadata, I turned to dynamic and things have been going quite well. Unfortunately, there was a hurdle to getting the JSON to look the way I wanted.

If you start playing around with serializing ExpandoObject to JSON, you will probably start finding a number of options. The easiest solution to find is the one that comes with .NET, JavaScriptSerializer under System.Web.Script.Serialization. It will happily serialize an ExpandoObject for you, but it probably won’t look the way you expect. Your searches will probably vary, but I found Newtonsoft’s JSON.NET, which handled ExpandoObject right out of the NuGet box. Then I stumbled on ServiceStack.Text (also “NuGettable”). While it does even weirder things than the .NET serializer with ExpandoObjects, it supposedly does them very fast.

Test code

dynamic test = new ExpandoObject();
test.x = "xvalue";
test.y = DateTime.Now;

BCL JavaScriptSerializer (using System.Web.Script.Serialization;)

JavaScriptSerializer javaScriptSerializer = new JavaScriptSerializer();
string jsonOfTest = javaScriptSerializer.Serialize(test);
// [{"Key":"x","Value":"xvalue"},{"Key":"y","Value":"\/Date(1314108923000)\/"}]

Not quite what I was looking for but it makes sense if you realize that ExpandoObject plays very nicely with IDictionary<string, object>. By using some code borrowed from StackOverflow (not the accepted answer, but I like it) and theburningmonk.com, you put together a custom serializer for ExpandoObject and you can get something more typical of what went into assembling the object.

/// <summary>
/// Allows JSON serialization of Expando objects into expected results (e.g., "x: 1, y: 2") instead of the default dictionary serialization.
/// </summary>
public class ExpandoJsonConverter : JavaScriptConverter {
    public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer) {
        // See source code link for this extension method at the bottom of this post (/Helpers/IDictionaryExtensions.cs)
        return dictionary.ToExpando();
    }
    public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer) {
        var result = new Dictionary<string, object>();
        var dictionary = obj as IDictionary<string, object>;
        foreach (var item in dictionary)
            result.Add(item.Key, item.Value);
        return result;
    }
    public override IEnumerable<Type> SupportedTypes {
        get {
            return new ReadOnlyCollection<Type>(new Type[] { typeof(ExpandoObject) });
        }
    }
}

JavaScriptSerializer javaScriptSerializer = new JavaScriptSerializer();
javaScriptSerializer.RegisterConverters(new JavaScriptConverter[] { new ExpandoJsonConverter() });
jsonOfTest = javaScriptSerializer.Serialize(test);
// {"x":"xvalue","y":"\/Date(1314108923000)\/"}

Newtonsoft Json.NET

string jsonOfTest = Newtonsoft.Json.JsonConvert.SerializeObject(test);
// {"x":"xvalue","y":"\/Date(1314108923000-0600)\/"}

That worked exactly as I expected. If I can get JSON.NET to work consuming JSON under MonoTouch, it will make my life quite easy; more to come on that.

ServiceStack.Text

string jsonOfTest = JsonSerializer.SerializeToString(test);
// ["[x, xvalue]","[y, 8/23/2011 08:15:23 AM]"]

ServiceStack’s JSON serialization system does something similar to the .NET JavaScriptSerializer, but not quite the same. I haven’t spent enough time with ServiceStack to know how this syntax will work out on consumption by another deserializing system, but I suspect this may be something that only ServiceStack would handle correctly.

Unfortunately, the author of the project was nice enough to confirm that ServiceStack.Text does not currently afford the same extensibility as the .NET JavaScriptSerializer for overriding its default behavior in this situation. He did welcome a pull request which I will look into.

ServiceStack.Text also doesn’t appear to support deserializing into an ExpandoObject as this resulted in an empty object.

dynamic testDeserialization = ServiceStack.Text.JsonSerializer.DeserializeFromString<ExpandoObject>(jsonOfTest);

I haven’t confirmed if ServiceStack.Text deserializing works under MonoTouch yet. If it does, it would be worthwhile to have it running API JSON generation as well as the client-side JSON consumption since there is evidence it performs quite nicely.

UPDATE

I slapped together a new MonoTouch project in MonoDevelop and tossed in ServiceStack.Text’s DLLs with a few bits of code and confirmed it works great for a deserializing JSON into a pre-defined object.

public class TestClass {
    public int x { get; set; }
    public string x { get; set; }
}
TestClass result = ServiceStack.Text.JsonSerializer.DeserializeFromString<TestClass>("{\"x\":3,\"y\":\"something\"}");
Console.WriteLine("result: x={0}, y={1}", result.x, result.y);
// result: 3, something

UPDATE (2012-04-05)

I missed a blog entry on the author’s blog describing how to get Json.NET to output DateTime in different ways. For example, if you prefer the ISO-8601 output, you would be able to tell it to use the IsoDateTimeConverter. When I went to update the test project, the latest version of Json.NET (4.5 Release 1) now defaults to ISO-8601. Since I don’t want to risk break an existing API, I tweaked the DateFormatHandling to make the output match the old default. Examples are hard to find since this is such a new release, so I slapped one together and submitted it to the Json.NET docs (now on GitHub).

Newtonsoft.Json.JsonSerializerSettings settingsWithMicrosoftDateFormat = new Newtonsoft.Json.JsonSerializerSettings() { DateFormatHandling = Newtonsoft.Json.DateFormatHandling.MicrosoftDateFormat };
string jsonOfTest = Newtonsoft.Json.JsonConvert.SerializeObject(test, settingsWithMicrosoftDateFormat);
// {"x":"xvalue","y":"1333640263042-0600"}

Unfortunately, even Json.NET 4.5 is appending a timezone offset to the DateTime serialization that isn’t found in the .NET implementation. I’ll look into a custom implementation of DateTimeConverterBase and I have posted this as a question on StackOverflow.

Additional Notes

I haven’t played much with what problems may arise with the various representations of DateTime objects on the consumption side, but they definitely all handled it differently here.

Source Code

To get all the code in an ASP.NET MVC project (download, load solution, hit F5 [I hope]), check out the bitbucket.org repository for this post.