Click here to Skip to main content
Licence CPOL
First Posted 1 Apr 2009
Views 11,809
Bookmarked 23 times

JSON and C# using Generics and Delegates

By | 1 Apr 2009 | Technical Blog
JSON is a method for transferring data, similar to XML and other formats. There are many advantages to using this method. It is human readable, and it translates easily to objects on the client side of the browser.
A Technical Blog article. View original blog here.[^]

JSON or JavaScript Object Notation, is a method for transferring data, similar to XML and other formats. There are many advantages to using this method. It is human readable, and it translates easily to objects on the client side of the browser.

With JSON, I can declare an array like this:

var myArray = ["this","that","the other"]; 

The object notation is even simpler. Imagine a C# "Person" class:

public class Person 
{
   public int ID { get; set; } 
   public string FirstName { get; set; }
   public string LastName { get; set; }
   public string SSN { get; set; }
}

If I want to express this data to the client, I can do something like this:

var person = { 
   "ID" : "1", 
   "FirstName" : "Jeremy",
   "LastName" : "Likness",
   "SSN" : "111-222-3333" 
};
alert("My name is " + person.FirstName + " " + person.LastName); 

One criticism of the WebForms view engine (the one most of you are using in .NET ... while other view engines such as NVelocity have existed as part of the MonoRail project, Microsoft only recently released the MVC release candidate which introduces the Model-View-Controller view engine) is that it overburdens the client when rendering simple tags. If you have compared <input type="text"> with an <asp:TextBox>, you get the point.

Furthermore, developers tend to get lost in the "ease" of the AJAX framework and often fail to consider the performance implications of wrapping, say, cascading drop downs in an update panel. A much easier method is to serialize the values and bind them using a third-party tool like JQuery instead.

While I was working on this framework, I realized it would be quite simple to serialize my domain objects for JSON. I wanted something generic, that didn't rely on reflection for abstraction, and gave me full control over which attributes to render without having to dirty my domain model with attributes or other declarations for something that is entirely UI-related.

The result? A "JSONObject" with a Serialize() method that emits the JSON code. Here is the class:

namespace MyJSON 
{
    /// <summary>
    ///     Delegate to determine how a value is pulled from the object
    /// </summary>
    /// <param name="instance">The instance to pull the property from</param>
    /// <param name="property">The value of the instance</param>
    /// <returns>The string representation of the value for that property</returns>
    public delegate string PropertyValue<t>(T instance, string property);

    /// <summary>
    ///     Class to help serialize AirWatch objects to the client
    /// </summary>
    public class JSONObject<T> 
    {        
        /// <summary>
        ///     The object to serialize
        /// </summary>
        private readonly T _object;

        /// <summary>
        ///     A list of properties to serialize
        /// </summary>
        private readonly List<string> _properties;

        /// <summary>
        ///     Reference to delegate to parse the value
        /// </summary>
        private readonly PropertyValue<t> _propValue; 
               
        /// <summary>
        ///     Constructor for JSON object
        /// </summary>
        /// <param name="instance">The entity instance</param>
        /// <param name="properties">The list of properties to serialize</param>
        /// <param name="propValue">The method to extract the property value</param>
        public JSONObject(T instance, IEnumerable<string> properties, 
		PropertyValue<T> propValue)
        {
            _object = instance;
            _properties = new List<string>(properties);
            _propValue = propValue; 
        }

        /// <summary>
        ///     Serialize to the JSON representation
        /// </summary>
        /// <returns>The JSON representation</returns>
        public string Serialize()
        {
            StringBuilder json = new StringBuilder();

            json.Append("{");

            bool first = true;

            foreach(string prop in _properties)
            {
                if (first)
                {
                    first = false;
                }
                else
                {
                    json.Append(","); 
                }
                string value = _propValue(_object, prop);
                json.Append(string.Format("\"{0}\":{1}", prop, EncodeJsString(value))); 
            }

            json.Append("}"); 

            return json.ToString(); 
        }
   }
}

The JSON object takes a type of T which can be anything. The constructor takes in "T", along with an array and a delegate. The array is a list of strings describing the properties I am interested in. For example, I may only want to send id and last name because I don't use the other attributes in my client script. Finally, the delegate. The signature is simple: given the attribute and the entity, what is the value as a string? This allows me to inject the logic to map the UI attributes to the domain attributes. The easiest way to send the data is to realize it as a string and then manipulate it appropriately on the client. After all, JavaScript itself doesn't "know" about any of our complex objects.

The simplest use would be simply to send one of my Person objects and show the name. For example:

<b>ID: </b> <span id="personId"> </span>
<b>Name: </b> <span id="personName"> </span>

In my own script, I simply make my JSON object and serialize it out:

JSONObject<Person> jsonPerson = new JSONObject<Person>(
   new Person { ID = 1, LastName="Likness" },
   new[] { "id", "name" }, 
   (entity,property) => property.Equals("id") ? entity.ID : entity.LastName
);
Page.ClientScript.RegisterClientScriptBlock(GetType(),GetType(),
   string.Format("var person={0}",jsonPerson.Serialize()),true);

Finally, I need something to wire it in for me, I'll choose JQuery ...

<script type="text/javascript">
   $(document).ready(function(){
      $("#personId").html(person.id);
      $("#personName").html(person.name); 
   });
</script>

The script block takes a type (I'm just using the type of the page or control I'm in), a key (this should be unique per script block ... I'm using the type again here but could have it strongly typed or set as a const, etc), the JavaScript to emit (from our serialize method on the JSON object), and then the true tells it to wrap my script tags because I haven't done it myself.

These objects can come back as the result of a callback (just assign them to the window.var object so they are globally accessible and use an eval) for dynamic binding, or you might simply render an array of objects and then do something like this:

for (var x = 0; x < window.json_options.length; x++) {
            var option = window.json_options[x];
            $("#selBox").append("<option value=\"" +
                option.id + "\"" + +(first ? " checked=\"checked\"" : "") 
			+ ">" + option.value + "</option>");
        }

which will bind the drop down list. If you are fighting with the urge to cry "hack" because we're wiring in HTML ... remember, this is what ASP.NET does under the covers for you. In the end, controls are complex text rendering engines that emit fragments the browser can manage.

Of course, the next step was to make a JSONList<T> and I purposefully left the encode script out. You can Google some solutions for that.

Jeremy Likness

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Jeremy Likness

Architect
Wintellect
United States United States

Member

Follow on Twitter Follow on Twitter
Jeremy Likness is a Microsoft Silverlight MVP who works as Project Manager and Senior Consultant for Wintellect with 15 years of experience developing enterprise applications. He has worked with software in multiple verticals ranging from insurance, health and wellness, supply chain management, and mobility. His primary focus for the past decade has been building highly scalable web-based solutions using the Microsoft technology stack with a focus on Silverlight since version 2.0.
 
Prior to Wintellect, Jeremy was Director of Information Technology and served as development manager and architect for AirWatch, LLC, where he helped the company grow and solidify its position as one of the leading wireless technology solution providers in the United States by managing the development of their product portfolio that includes public HotSpot solutions and a management console for enterprise grade wireless networks, mobile devices, and their consumers. A fluent Spanish speaker, Jeremy served as Director of Information Technology for Hispanicare, where he architected a multi-lingual content management system for the company's Hispanic-focused online diet program. Jeremy accepted his role there after serving as Development Manager for Manhattan Associates, a software company that provides supply chain management solutions.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
GeneralNice atricle PinmemberNajmul Hoda9:06 7 Apr '09  

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Mobile
Web02 | 2.5.120517.1 | Last Updated 1 Apr 2009
Article Copyright 2009 by Jeremy Likness
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid