Tip/Trick: Building a ToJSON() Extension Method using .NET 3.5

Earlier this year I blogged about a new language extensibility feature of C# and VB called "Extension Methods". 

Extension methods allow developers to add new methods to the public contract of an existing CLR type, without having to sub-class it or recompile the original type.  In doing so they enable a variety of useful scenarios (including LINQ).  They also provide a really convenient way to add a dash of "syntactic sugar" into your code.

Over the last few months I've been making a list of cool extension methods that I plan to sit down and implement when I get some free time (not sure when that is... but at least I can still have fun coming up with the ideas!)  Two of the scenarios I added to my extension method list were easy methods to automate generating JSON (JavaScript Object Notation) or XML serialization strings for any .NET object. 

Simple Scenario: The ToJSON() extension method

Assume I had a Person object defined like below (note: I'm using the new automatic properties feature to implement it):

I'd like to then be able to initialize a collection of Person objects and programmatically retrieve a JSON string representation of them by just calling a ToJSON() extension method on it like below:

This would work just like the built-in ToString() method on the Object class in .NET today - except that it would generate a JSON-format representation of the collection that I could use for AJAX scenarios on the client:

Note: Clicking on the hour-glass in the debugger above allows us to bring up the Text Visualizer in VS to see a clean version of the JSON serialization:

This string format could then be used within JavaScript on the client to instantiate an appropriate JavaScript object that represents my collection (note: ASP.NET AJAX has a built-in JavaScript library to support this).

Implementing the ToJSON Extension Method

Implementing a basic ToJSON() extension method is pretty simple.  All I needed to-do was use the JavaScriptSerializer class in the System.Web.Script.Serialization namespace, and define two extension methods like below. One of the methods serializes an object graph any levels deep, the other is an overloaded version that allows you to optionally constrain how deep it recurses (for example: ToJSON(2) would serialize only 2 levels deep in the object graph).

Note that the ToJSON() extension methods above are defined for type "Object" - which means they can be used with all objects in .NET (not just collections).  This means that in addition to calling .ToJSON() on collections like I did above, I could also have called ToJSON() on individual Person objects, as well as any other .NET datatype.

To use the extension method, all I need to-do is add a using statement at the top of my program that references the namespace it was defined within:

VS 2008 then takes care of providing intellisense and compile time support for it to all objects:

Note: In addition to the JavaScriptSerializer class, .NET 3.5 also now includes a new System.Runtime.Serialization.DataContractJsonSerializer class that you can use for JSON serialization/deserialization.

Summary

Hopefully the above sample provides a simple example of how you can easily encapsulate useful functionality into extension methods.  Overtime I expect that we'll start to see some nice utility libraries come out that provide helpful extension methods like above. 

I'd be curious to see suggestions for other common scenarios you think should be packaged up into re-usable extension methods (feel free to use the comments of this post to suggest them).  We can then figure out how to get a good CodePlex project created that bundles up some of them together into one library to easily use.

Hope this helps,

Scott

P.S. Please check out my Tips/Tricks and Tutorials page to find other useful ASP.NET and .NET posts I've done.

30 Comments

  • Hey Scott,

    Don't let Bill see this, you put him second!!! LOL.

    Great stuff! Thanks!

  • I've found the new JSON generation mechanisms in .NET 3.5 to be quite useful indeed. I wrote a similar extension method that uses the XmlJsonWriter directly, with reflection. This enables me to easily translate anonymous objects to JSON. In the rather common case that you want to push only part of an object graph down to your script layer, it's a great strategy. It also enables you to create more JavaScript-friendly property names on the fly.

    var jsonString = users.Select(u => new { id = u.Id, name = u.FullName }).ToJsonString();

  • Hi,

    One extension method I've found useful in web development is an EnsureIdSet() method. By adding this to Controls, you can ensure that either an ID has been specifically set or the ID is set to the auto-generated 'ctlXX' ID. (Without this, the ID may not be set until much later in the page lifecycle.)

    Cheers,

    Geoff

  • Too cool! Can't wait to mess with this. Have been waiting for ages for this kinda thing :-)
    Keep up the good work guys!

    /M

  • That's a beautiful class. I just had to write my own the other day for a 2.0 project. I plan on porting that project over to 3.5, but we're a little busy right now (and it's 30k lines of code, so I'm in no rush).

    I have to say that I'm somewhat torn in my feelings about 3.5 and VS2008. I've been using it in a production environment for a couple of months now, and I'm loving it for the most part, but there still is that 25% that's missing.

    From the small things: (public static T FindControl(this Control ctrl)) to the large things (Entity framework fitting into a proper architecture with proper seperation for DAL, BLL and other layers).

    All in all (and yes, this is the end of my rant)... great job, great product. Ooh, another 'beef' I have with 2008... right-click on a MasterPage and click "Add Content Page"... and it spits out "WebForm1.aspx" <-- nasty... Why not prompt for a name?

  • Again, to bust your chops: ToJSON should be ToJson :)

  • Scott, tell me - why do you use deprecated method to serialize? Only for simplicity?

  • Sweet! I also built my own JSON serializer for 2.0 (and 1.1). I don't recall the size; however, it may be even bigger than 30k. I've seen integers (numbers in js) both wrapped and unwrapped in double quotes and I have not found the 'standard' anyware online. Can you point me to the defacto? One other thing: I agree with Nullable: I perfer ToJson over ToJSON. Thanks Scott...

  • Sorry I must be missing something, I don't understand how the person class or people list knows to use the extension class. There isn't any code other than the using statment that links the class is that all that is required ?
    Trivial - I think you mean magnifying glass rather than hour-glass. I should not point things like that out but it is a friendly point.

    Please can you do a demo of disconnected Linq to sql. I am almost busting to see how you recommend using linq to sql via say WCF to a smart client type app be it an xbap or silverlight app.

  • This May I came up with a set of extension method that exposes a IEnumerable interface that returns items back in a random order.

    I provided two extension methods:

    public static T NextRandom(this IEnumerable source)

    Returns a random back every time it is call without regard to whether its been return before.

    public static IEnumerable Radomized(this IEnumerable source)

    Returns every item of a IEnumerable back exactly once in a random order. Uses a list to track remaining items, NextRemaining to return from that list, and a custom iterator using yield to return the interface.

    Sample Solution is here:
    http://foreachdev.net/blog/2007/05/14/linq-part-2how-to-be-random-with-linq-to-objects/

  • This is GREAT! I am a .NET developer, but was always interested in a more Open Microsoft. I donated the domain OpenAjax.Org to foster greater collaboration, and I am VERY excited that Microsoft has joined the OpenAjax Alliance. I am also created a project on JSON.Com to promote JavaScript Object Notation, and I thank you for putting this into .Net as well.

  • Hi Derek,

    >>>>>> I'm getting an obsoletion warning though, which is somewhat concerning. Is this class on the chopping block?

    The JavaScriptSerializer is currently marked obsolete - although I'm not sure why (was going to ask someone about this today). It is very handy. Note that obsolete doesn't mean removal in .NET 3.5 - it will be supported at least another version or two (or longer if I can convince them ).

    Thanks,

    Scott

  • Scott,
    Question for u about Extension Methods; Do you have access to the private fields + methods of the class you're extending? e.g. IMHO developers should have access to the HasMoreRows property of a SqlDataReader (subtle hint Scott) but couldn't do it without reflecting it; can this be done with extension methods?
    ps Thanks for another great tip!!!

  • Hi Ariel,

    >>>>>> Sorry I must be missing something, I don't understand how the person class or people list knows to use the extension class. There isn't any code other than the using statment that links the class is that all that is required ?

    If you add a using statement for a namespace with an extension method, the compiler will automatically enable the extension methods for those types that the extension method extends. In the example above I was extending "object" - which is why it worked on everything.

    >>>>>>> Trivial - I think you mean magnifying glass rather than hour-glass. I should not point things like that out but it is a friendly point.

    Good catch!

    >>>>>>>> Please can you do a demo of disconnected Linq to sql. I am almost busting to see how you recommend using linq to sql via say WCF to a smart client type app be it an xbap or silverlight app.

    Yep - this is definitely on my list todo. I'll get to it later this month I hope.

    Hope this helps,

    Scott

  • Hi Marc,

    >>>>>> Question for u about Extension Methods; Do you have access to the private fields + methods of the class you're extending? e.g. IMHO developers should have access to the HasMoreRows property of a SqlDataReader (subtle hint Scott) but couldn't do it without reflecting it; can this be done with extension methods?

    Extension methods themselves only allow you to work against public members. If you wanted to access private members you'd need to-do it via internal reflection inside the extension method.

    Hope this helps,

    Scott

  • no words, just cool.
    tnk

  • Hi Scott,

    I have a requirement where I need to pull only selected columns based on user selection from the database instead of pulling all the columns and then making them invisible. Can this be done in LINQ to SQL? Is it possible to dynamically (based on user selected variable list) return variables in LINQ?

    For example, if I have 100 columns for an order in my orderdetails table and in the application preference the user can check the columns he/she interested in and then pull on those from the database or say all the columns are retrieved for some other reason, but when the user filters or does any querying operation on the orderdetails table (LINQ to SQL) I should return only the interested columns (dim a = from a1 in OrderDetails Select a1) in the select clause. Is this possible in LINQ?

    Thanks,
    Ram

  • Hey Scott, great post.
    Here is a handy extension method that applies some action to every element in an enumrable:
    void ForEach&lt;T&gt;(this IEnumerable&lt;T&gt; elements, Action action);
    FWIW, I've used extension methods to simply code quite a bit. I wrote a little WinForms app here that scans a forum I moderate for spam and deletes the spam posts automatically. I used extension methods on System.Windows.Forms.HtmlElement for determining whether an HTML element contained spammy text. The resulting code is quite clean and extension methods are largely to thank for that.

  • >>>>>>>Extension methods themselves only allow you to work against public members. If you wanted to access private members you'd need to-do it via internal reflection inside the extension method.

    Can you please provide and example of how can I use the internal extension to work with the private members of the class. Thanks in advance

  • Scott,

    Thank you for your fantastic articles.

    I'm playing with LINQ and WFC to build a little DB-driven AJAX app. I've hit a wall wrt sending LINQ objects from my WFC services -- they don't serialize automatically to JSON. The approach you outlined above does the trick, but then I need to handle deserialization myself on the client-side.

    Is there a way to allow WFC services to pass back unadulterated LINQ objects and collections, without decorating each LINQ class with [Serializable] or using this trick?

    Best,
    Mike

  • The question that I have regarding the Extension Methods is not related to JSON rather on the conflict resolving with same extension method on different assemblies. My question is as follows:

    I have checked the IL generated by the extension methods and it seems that it uses the .CompilerServices.ExtensionAttribute::.ctor() attribute. So this is not explicitly calling the static method at compile time rather calling the function at the runtime.

    It is very likely that many people will have their own implementation of the custom methods on classes like string, int, StringBuilder etc and it is likely that when you add third party dlls then same method may have the same signature in different assemblies. If they are used in code then this error is caught at compile time and easily resolved by explicitly calling the static method from the static class. But if the code is already compiled then adding such a dll in the same private dll path would create runtime ambiguous method error. But if we had pointed the IL to point to the static class then we would able to avoid such an issue since Namespace and class name combination would be very hard match.

    So a piece of code that uses one assembly and name import directive in header could run into such a problem where a assembly added later on with same namespace has same extended method. This is likely to happen on common classes like string.

    So my question is was this scenario taken into account when designing the IL generation for extension methods? How do we resolve this?

  • Nice one!

    I'm trying to use DataContractJsonSerializer. How can I serialize an object to a string instead of a FileStream though?

  • Is it possible to use linq with vs2005 today? Or is it just a feature of Oracs?

  • Hi Michael,

    >>>>>> Is it possible to use linq with vs2005 today? Or is it just a feature of Oracs?

    Unfortunately LINQ is a VS 2008/.NET 3.5 feature only.

    Sorry!

    Scott

  • So I just tried doing my first extension method and I guess I have a talent for picking out some exception. I would like to add a "ToHexString" to a type of byte[]. When I tried this out, I get the following message:
    'System.Array' does not contain a definition for 'ToHexString' and no extension method 'ToHexString' accepting a first argument of type 'System.Array' could be found (are you missing a using directive or an assembly reference?)
    So then I tried to make my extension take an argument of (this System.Array byteArray). But I don't know what do with the System.Array to test if it is a Byte Array or some other array or how to get to the individual members... any help would be appreciated.

  • Hi Jeff,

    The problem you have is that you are trying to add the extension method to array. Instead you'd want to add it to byte array like so:

    public static string ToHexString(this byte[] byteArray) {

    }

    I just tried this out and confirmed it works.

    Hope this helps,

    Scott

  • Now I dont have to use Newtonsoft.Json. =)

  • that sounds cool, well with Extension methods you will be able to do all sort of crazy things you dont have to stop with Json, any kind of object serialization can be added.

    ps.: like the way you have put Bill is next to you.

  • Nice tip - thanks.

    As others have said you can do it with other serializations as well, so I had to try...

    using System.Xml.Serialization;
    using System.Text;
    using System.IO;
    using System.Windows.Markup;

    //Here's one for XML.

    public static string ToXML( this object obj )
    {
    XmlSerializer x = new XmlSerializer(obj.GetType());
    StringBuilder b = new StringBuilder();
    using (StringWriter wr = new StringWriter( b ))
    {
    x.Serialize(wr, obj);
    }
    return b.ToString();
    }

    //Heres a simple one for Xaml

    public static string ToXaml( this object obj )
    {
    return XamlWriter.Save(obj);
    }

    I really like C#.

  • that is cool, but i like to know if you could put constraints on extension methods so if a class is marked as non-serializable the method wont be availabled for that class. is it possible?

    ----------
    http://undocnet.blogspot.com

Comments have been disabled for this content.