Scott Hanselman

Serializing Objects as JavaScript using Atlas, JSON.NET and AjaxPro

July 5, '06 Comments [25] Posted in ASP.NET | Ruby | Javascript | TechEd | Speaking | Web Services
Sponsored By

Ajax is shiny. In our talk at TechEd, Patrick and I mentioned that our next plan was a dynamic endpoint for our financial services that spoke JSON to complement our "Dirty SOAP" endpoint. This would make auto-complete dropdowns and sortable grids REALLY easy when interfacing with our SDK that already supports a large message set for banking-type things like GetPayees, GetAccountHistory.

The first step to make this happen will be JSON serialization round-tripping. For example, I'd like to take this object (instance)...

public class Person

{

    public string firstName = "Scott";

    public string lastName = "Hanselman";

    public DateTime birthDay = new DateTime(1970, 1, 15, 1, 1, 0);

    public decimal moneyInPocket = 4.5M;

}

...and serialize it to JSON thusly:

{"firstName":"Scott", "lastName":"Hanselman", "birthDay": new Date(1213260000), "moneyInPocket":4.5}

I was already planning to create a JavaScript serializer as Corillian already has fixed-length, delimited, name-value pair and other serializers for any object.

I took a look at JSON.NET thinking it'd be a nice, lightweight serializer, and while it's cool on initial first glance, this release didn't pass the "fall into the pit of success" test for an object serializer.

UPDATE: Json.NET has been updated and now works as expected and includes helper methods to make the job simpler...new code below.

    1 using System;

    2 using System.IO;

    3 using System.Collections.Generic;

    4 using System.Text;

    5 

    6 namespace ConsoleApplication1

    7 {

    8     public class Person

    9     {

   10         public string firstName = "Scott";

   11         public string lastName = "Hanselman";

   12         public DateTime birthDay = new DateTime(1970, 1, 15, 1, 1, 0);

   13         public decimal moneyInPocket = 4.5M;

   14     }

   15 

   16     class Program

   17     {

   18         static void Main(string[] args)

   19         {

   20             Person p = new Person();

   21             string output =  Newtonsoft.Json.JavaScriptConvert.SerializeObject(p);

   22 

   23             output = output.Replace("Scott", "Fred");

   24             output = output.Replace("Hanselman", "Jones");

   25 

   26             Person anotherP = Newtonsoft.Json.JavaScriptConvert.DeserializeObject(output, typeof(Person)) as Person;

   27             Console.WriteLine(anotherP.firstName + " " + anotherP.lastName);

   28         }

   29     }

   30 }

   31 

Then I tried Ajax.NET 6.7.2.1 from Michael Schwarz...

    1 using System;

    2 using System.IO;

    3 using System.Collections.Generic;

    4 using System.Text;

    5 using Microsoft.Web.Script.Serialization;

    6 

    7 namespace ConsoleApplication1

    8 {

    9     public class Person

   10     {

   11         public string firstName = "Scott";

   12         public string lastName = "Hanselman";

   13         public DateTime birthDay = new DateTime(1970, 1, 15, 1, 1, 0);

   14         public decimal moneyInPocket = 4.5M;

   15     }

   16 

   17     class Program

   18     {

   19         static void Main(string[] args)

   20         {

   21             Person p = new Person();

   22             string output = AjaxPro.JavaScriptSerializer.Serialize(p);

   23 

   24             output = output.Replace("Scott", "Fred");

   25             output = output.Replace("Hanselman", "Jones");

   26 

   27             Person anotherP = AjaxPro.JavaScriptDeserializer.DeserializeFromJson(output, typeof(Person)) as Person;

   28             Console.WriteLine(anotherP.firstName + " " + anotherP.lastName);

   29         }

   30     }

   31 }

   32 

...but got this exception on the deserialization. When reflectoring through the source, this implies that null was passed to DateTimeConverter.Deserialize. I think an ArgumentNullException would have been clearer.

System.NotSupportedException was unhandled
  Message="Specified method is not supported."
  Source="AjaxPro.2"
  StackTrace:
       at AjaxPro.DateTimeConverter.Deserialize(IJavaScriptObject o, Type t)
       at AjaxPro.JavaScriptDeserializer.Deserialize(IJavaScriptObject o, Type type)
       at AjaxPro.JavaScriptDeserializer.DeserializeCustomObject(JavaScriptObject o, Type type)
       at AjaxPro.JavaScriptDeserializer.Deserialize(IJavaScriptObject o, Type type)
       at AjaxPro.JavaScriptDeserializer.DeserializeFromJson(String json, Type type)
       at ConsoleApplication1.Program.Main(String[] args) in C:\Documents and Settings\Scott\Desktop\ConsoleApplication1\ConsoleApplication1\Program.cs:line 29

However, I was able to get it to round-trip when I removed the DateTime. Not sure what's up with that.

Also interesting, Ajax.NET (AjaxPro) saved the Type/Assembly Qualified Name in the resulting JSON. I can see why they'd want to do that, but one of the nice things about JavaScript and JSON in general is the cleanliness and flexibility of the wire format. This could complicate things if I've got different CLR types on the server consuming the same serialized JSON from the client. It also serializes the DateTime in a different way than I'm used to.

{"__type":"ConsoleApplication1.Person, ConsoleApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null", "firstName":"Scott", "lastName":"Hanselman", "birthDay": new Date(Date.UTC(1970,0,15,9,1,0,0)), "moneyInPocket":4.5}

Moving to Microsoft's Atlas, similar and slightly simpler code works just fine like this:

    1 using System;

    2 using System.IO;

    3 using System.Collections.Generic;

    4 using System.Text;

    5 using Microsoft.Web.Script.Serialization;

    6 

    7 namespace ConsoleApplication1

    8 {

    9     public class Person

   10     {

   11         public string firstName = "Scott";

   12         public string lastName = "Hanselman";

   13         public DateTime birthDay = new DateTime(1970, 1, 15, 1, 1, 0);

   14         public decimal moneyInPocket = 4.5M;

   15     }

   16 

   17     class Program

   18     {

   19         static void Main(string[] args)

   20         {

   21             Person p = new Person();

   22             string output = JavaScriptObjectSerializer.Serialize(p);

   23 

   24             output = output.Replace("Scott", "Fred");

   25             output = output.Replace("Hanselman", "Jones");

   26 

   27             Person anotherP = JavaScriptObjectDeserializer.Deserialize(output, typeof(Person)) as Person;

   28             Console.WriteLine(anotherP.firstName + " " + anotherP.lastName);

   29         }

   30     }

   31 }

   32 

...giving the expected output of

{"firstName":"Scott", "lastName":"Hanselman", "birthDay": new Date(1213260000), "moneyInPocket":4.5}

Between these three JSON serializers and this simple test, only ATLAS "just worked." (Assuming my 'simple test' isn't flawed.) For now I'll use these Atlas assemblies for my JSON serialization needs, but it'd be nice if I could back-port the chunky parts of one of the other libraries to .NET 1.1 my for projects that can't use 2.0.

UPDATE: Json.NET and Atlas appear to work the same with this simple test.

Of course, the really sad thing is that John Lam or my boss will step in here any minute and remind us that in Ruby on Rails you can just say object.to_json and get JSON strings. Phooey!

As a totally unrelated aside, and for the purposes of starting a discussion - in the JSON.NET source code the author James Newton-King appears to have decompiled the source for System.Web.UI.Util.QuoteJScriptString from ASP.NET and included the decompiled source (with a few modifications) directly in JSON.NET's JavaScriptUtils.cs which is then licensed under the Creative Commons Attribution 2.5 License. 

UPDATE: James Newton King updated the JSON Framework and removed the CC license from the JavaScriptUtils.cs file as it wasn't his intent to release Microsoft code like that. He also included an explanatory comment. Seems like a reasonable solution to me.

The question is: When a useful function exists in the .NET Framework, but is marked internal/private, for whatever reason. Is it better to:

A. decompile it and include it in your code anyway (hopefully with an explanatory comment).
B. write your own from scratch.
C. use the internal/private/whatever method via Reflection.

Discuss.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. I am a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web
Wednesday, July 05, 2006 5:23:23 AM UTC
Isn't making these methods internal, short-sightedness on Microsoft's part in the first place? Can't really blame Microsoft developers, though. Almost all of us suffer from the holier-than-thou/better-programmer-than-thou/i-know-better syndrome.

In any case, reflection could mean a performance hit, but that would be my choice.

Sachin
Sachin Rao
Wednesday, July 05, 2006 6:01:01 AM UTC
Well, there's two steps to reflection, right? There's the getting of the MethodInfo, and the Invoking. They are distinct. I think if you cached the MethodInfo, it wouldn't be too bad, right?
Scott Hanselman
Wednesday, July 05, 2006 6:16:11 AM UTC
Using reflection will break ASP.NET apps that run in less than hightrust.
Andy Miller
Wednesday, July 05, 2006 7:47:08 AM UTC
Andy, do you care about CAS that much? I'm more concerned with ACLs and Windows-level security, but perhaps that's short-sighted of me.
Scott Hanselman
Wednesday, July 05, 2006 8:27:43 AM UTC
I completely forgot about the QuoteJScriptString method, I've been using it for years. You can essentially do the same thing by using the JavaScriptObjectSerializer Atlas method, although Atlas had not yet been released when I wrote Json.NET.

I corrected the bug you pointed out on my blog and here. I also added some shortcut methods to the JavaScriptConvert class for deserializing. I have mainly been using the library myself for serializing objects so there were a few bugs left. The 1.0.1 version is now on the downloads page - http://www.newtonsoft.com/downloads/#json
Wednesday, July 05, 2006 9:43:46 AM UTC
Hi Scott,

Ajax.NET Professional will use different JSON representations for communication direction. What I didn't implement is the "new" operator Atlas is using. Atlas has only implemented the "new" operator for the Date object, or is it possible to use own created classes (from JavaScript code)? No, so you are comparing apple with ajax... ;)

The __type property is used to use any inherited class as argument, too, which is not working in Atlas!

See my post here:

http://weblogs.asp.net/mschwarz/archive/2006/07/05/Serializing-Objects-as-JavaScript-using-Atlas-and-AjaxPro.aspx


Regards,
Michael
Wednesday, July 05, 2006 10:02:17 AM UTC
That's true enough isn't it? Exposing internal methods via reflection can be exploited, i guess. The next question would then be how real is that risk? That, unfortunately, is subjective.

Are the other options worth considering? I personally would not wanna write my own version or copy. Also, I'm not sure if there would be any IP issues if we were to copy. Does anyone know?

Can it be the silver bullet?

Sachin Rao
Wednesday, July 05, 2006 12:12:41 PM UTC
Scott,
The question about CAS must be kept in perspective. When you are writing a generic library, then you need to take steps to minimize your runtime requirements. The minute you say that you have to run in High or Full trust, you immediately remove a large segment of potential users. I am seeing more and more hosting companies move to running Medium Trust environments based on the recommendations of Microsoft.

So given that we are discussing a generic web library I think you should exclude reflection as an option (assuming the rest of the library will run in a partial trust environment). I also get a sour taste from the thought of copying/modifying someone else's intellectual property (although it might be ok if they found the comparable MONO code and used that as a basis - depending on the license compatability - and given the correct code attributions.)

Wednesday, July 05, 2006 2:00:19 PM UTC
The really nice thing about Ruby is that it makes obj.to_json very easy to implement:

class Object
def to_json
# implementation
end
end

This makes a private patch to Object at runtime and is how Rails injects a new method into Object.

Wednesday, July 05, 2006 2:48:47 PM UTC
If you're looking for a JSON library for .NET 1.1 projects then check out Jayrock over at http://jayrock.berlios.de. It does serialization to JSON only at the moment. In other words, you can output JSON given a .NET type but not go back. But the project isn't done yet, which is why it's Open Source and why contributions would be welcome! :)
Wednesday, July 05, 2006 3:31:01 PM UTC
Michael, I've read your post twice and I still don't understand. I want to round-trip a dateTime object, that's all. I don't care if it's created on the server or client, I want to round-trip the objects. I expect the objects on both sizes to use the same wire protocol. Is this the wrong thign to want?
Scott Hanselman
Wednesday, July 05, 2006 4:14:58 PM UTC
For me, you've got to respect private/internal as being an indication from the developer of that code that this method is not part of the interface they are promising to hold to for you. It's not part of the Contract which they are offering. If you extract the code bit and use it, then at least you have confidence in the interface not changing when a Service Pack for the framework comes along.

Obviously you need to understand what the code is doing, and since you should understand such the "write your own" option seems simply repetitive.
Wednesday, July 05, 2006 4:25:18 PM UTC
You might be interested in following Script# (http://www.nikhilk.net/ScriptSharpIntro.aspx). Seems like it may be up your alley for future projects.
Darren Kopp
Wednesday, July 05, 2006 6:36:32 PM UTC
Hi Scott, yes, you may be right with the statement that you want to use JSON in both directions the same way, but then you should not compare features and frameworks that want to do this. AjaxPro will not do this because you will have thousands JavaScript constructors that are not working with Atlas and other discussed libs. So, Atlas is not working correct if you want to use "new ActiveXObject(...)" as an argument, and it is not working if you are using "new AjaxPro.Web.DataSet(...)", or?

With AjaxPro I'd like to develop on the client side JavaScript with the power of .NET. So, I'm currently working on a newer release that will not use the "new" statement because of security requests.

Regards,
Michael
Wednesday, July 05, 2006 7:37:56 PM UTC
> public decimal moneyInPocket = 4.5M;

Non-C# programmer types (who don’t know that the “M” means “decimal type”) might be tempted to mug you at the next conference they see you at.
Thursday, July 06, 2006 2:36:40 AM UTC
Regarding the question of what to do...

Decompiling and then redistributing the code is against the license. Sorry, I work for Microsoft, I have to say that. :)

Calling via private reflection is not only a CAS issue, it's also potentially a performance issue. Private reflection can be on the order of two magnitudes slower than public reflection. See http://hyperthink.net/blog/CommentView,guid,14736081-2589-474a-b867-85fa1c33f4d7.aspx for more info.

So, what are you left with? Write on yourself, and open source it. :)
Thursday, July 06, 2006 5:06:51 AM UTC
If you use any sort of framework the primary goal of a JSON parser would have to be round tripping, so I'd say that's way more important than arbitrary types serializing. Without that any framework is truly worthless... it only makes sense that a framework would serialize the data in a way that it can read it back. Duh!

Brad - I doubt that making a single Reflection call per serialization/deserialization will have a significant performance impact on an ASP.NET Web application which internally must be using many, many Reflection calls. In looping situations - sure, but one of calls in a non-chatty interface like this - unlikely.
Friday, July 07, 2006 12:02:16 AM UTC
"Andy, do you care about CAS that much?" I sell software widgets that run on ASP.NET. As Joe mentioned more and more hosts (at least shared hosts) are running in medium trust, which by default prevents code that uses reflection from running. So...yes.

I think I must be missing something in your conversation with Michael. I think you are saying you want to round trip an object server-to-client-to-server and get the same thing back. Michael seems to think you want to round tip an object server1-to-client-to-server2 where server1 and server2 are running two different libraries. Did I misunderstand?
Andy Miller
Friday, July 07, 2006 9:17:13 AM UTC
Andy, the big difference is that a JSON serialized string in Ajax.NET Professional cannot be used to get the corresponding .NET object back. The serialized JSON string is different i.e. if you use the DateTime object.

1) new Date(...);
2) {"Year":2006,"Month":4,"Day":20,...}

What you can do is running an ASP.NET web site that will get the JSON string from web server (serialize a .NET object) and use this object as a new argument for another AjaxMethod (deserialize). This is working great if you have a look at my example page:

http://munich.schwarz-interactive.de/datatype.aspx
Friday, July 07, 2006 4:52:36 PM UTC
Michael - with all due respect, you're not going to be able to argue that not-round-tripping serialized types is a good thing. It's not. It's a large, fundamental missing feature/flaw on the library.

a == b == a

Scott Hanselman
Friday, July 07, 2006 6:57:23 PM UTC
I will send you some examples where it is not working with Atlas or Json.Net, too. They are not working if you go more in detail.

The round-tripping of serialized types is working when round-tripping is including the client-side JavaScript code. ;)
Saturday, July 08, 2006 12:15:15 AM UTC
>> remind us that in Ruby on Rails you can just
>> say object.to_json and get JSON strings.

Between reflection and C# 3.0's extenstion methods you should be able to do this in C#:

public static class JsonExtension {
public static string ToJavaScriptObjectNotation(
this object obj) {
// ... Create StringBuilder,
// ... reflect on object,
// ... return strBld.ToString();
}
}

Customer customer = new Customer { Name="Scott", ... }
customer.ToJavaScriptObjectNotation();
Saturday, July 08, 2006 5:25:45 AM UTC
Scott, I took about 15 minutes to implement a very simple ToJavaScriptObjectNotation C# 3.0 extension method. It works with your Person class and probably not much more but I think it demonstrates feasibility. Check it out:

http://keithhill.spaces.msn.com/blog/cns!5A8D2641E0963A97!515.entry
Monday, July 10, 2006 8:32:44 AM UTC
Hi, I've updated my AjaxPro lib to allow "new Date" parsing, too. Download the latest version at http://www.ajaxpro.info/.

I did some more testing with some different data types, see post at http://weblogs.asp.net/mschwarz/archive/2006/07/10/Serializing-Objects-as-JavaScript-using-Atlas-and-AjaxPro-_5B00_Part-2_5D00_.aspx.
Saturday, July 15, 2006 10:19:16 AM UTC
Reflection isn't really an option. There are the security issues (a lot of hosts only offer medium trust) as well as performance. The method isn't called once per request but rather once per string, which there could many when serializing a large object.

Anyhow, I've released a new version of Json.NET with my own escape string method that improves on what was there, both in readability and performance. I had always been afraid to do my own after seeing how complicated the Microsoft version was but there was surprisingly little to it.
Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.