Showing posts with label JSON. Show all posts
Showing posts with label JSON. Show all posts

Friday, October 17, 2014

Benchmarking .NET JSON Serializers

using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace PlayJsonSerializers
{
    class Program
    {
        static void Main(string[] args)
        {
            Func<object,string> netJson = o => NetJSON.NetJSON.Serialize(o);
                
            Func<object,string> jil = o => Jil.JSON.Serialize(o);
            
            Func<object, string> ssText = o => ServiceStack.Text.JsonSerializer.SerializeToString(o);

            // ASP.NET Web API default JSON serializer
            Func<object, string> jsonNet = o => Newtonsoft.Json.JsonConvert.SerializeObject(o);

            var aspNetWebMvcDefaultJsonSerializer = new System.Web.Script.Serialization.JavaScriptSerializer();
            Func<object, string> javaScriptSerializer = o => aspNetWebMvcDefaultJsonSerializer.Serialize(o);
            // To replace ASP.NET MVC JSON serializer: http://www.ienablemuch.com/2014/10/aspnet-mvc-return-json-without-using-jsonresult.html


            var jsSerializers = new Dictionary<string, Func<object, string>>()
            {
                { "NetJSON", netJson },                
                { "Jil", jil },
                { "ServiceStack.Text", jil },
                { "Json.NET", jsonNet },
                { "ASP.NET MVC Default", javaScriptSerializer}
            };


            Action<string,object> benchmark = (description,o) =>
            {
                const string netJsonReplacedWithJsonNet = "NetJSON Replaced with Json.NET";

                var fallbackJsonSerializer = jsonNet;

                bool isAnonymousType = o.GetType().IsAnonymousType();

                Console.WriteLine(description);
               
                Console.WriteLine("Serialization");
                foreach (var item in jsSerializers)
                {
                    if (isAnonymousType && item.Key == "NetJSON")
                        fallbackJsonSerializer(o).Show(netJsonReplacedWithJsonNet);
                    else
                        item.Value(o).Show(item.Key);
                    
                }

                Console.WriteLine("Speed");
                foreach (var item in jsSerializers)
                {
                    if (isAnonymousType && item.Key == "NetJSON")
                        ShowTime(netJsonReplacedWithJsonNet, delegate { for (int i = 0; i < 20000; i++) fallbackJsonSerializer(o); });
                    else
                        ShowTime(item.Key, delegate { for (int i = 0; i < 20000; i++) item.Value(o); });
                }

                Console.WriteLine(new string('-',80));
            };


            benchmark("Object with complete properties", 
                new Person { PersonId = 123456789, FirstName = "Linus", MiddleName = "Great", LastName = "Snoopy" });

            benchmark("Object with null properties", 
                new Person { PersonId = 123456789, FirstName = "Linus", LastName = "Snoopy" });


            benchmark("Object with inheritance",
                new Employee { PersonId = 123456789, FirstName = "Linus", MiddleName = "Great", LastName = "Snoopy", IsActive = true });

                        
            benchmark("Anonymous object with complete properties", 
                new { PersonId = 123456789, FirstName = "Linus", MiddleName = "Great", LastName = "Snoopy", IsActive = true });


            benchmark("Anonymous object. Property with inheritance",
                new { Greet = "Hello", Employee = new Employee { PersonId = 123456789, FirstName = "Linus", MiddleName = "Great", LastName = "Snoopy", IsActive = true } });
                            
        }


        static void ShowTime(string jsSerializer, Action task)
        {
            var sw = Stopwatch.StartNew();

            task();

            sw.Stop();
            var milliseconds = (long)sw.ElapsedMilliseconds;
            Console.WriteLine("{0, 30}: {1}", jsSerializer, milliseconds);
        }
    }

    public class Person
    {
        public int      PersonId    { get; set; }
        public string   FirstName   { get; set; }
        public string   MiddleName  { get; set; }
        public string   LastName    { get; set; }
    }

    public class Employee : Person
    {
        public bool IsActive { get; set; }
    }

    static class StringExtension
    {
        internal static void Show(this string s, string description = "")
        {
            Console.WriteLine("{0, 30}: {1}", description, s);
        }
    }

    
    static class TypeExtension
    {
        // http://www.jefclaes.be/2011/05/checking-for-anonymous-types.html
        static internal bool IsAnonymousType(this Type type)
        {
            Debug.Assert(type != null, "Type should not be null");

            // HACK: The only way to detect anonymous types right now.
            return Attribute.IsDefined(type, typeof(System.Runtime.CompilerServices.CompilerGeneratedAttribute), false)
                       && type.IsGenericType && type.Name.Contains("AnonymousType")
                       && (type.Name.StartsWith("<>", StringComparison.OrdinalIgnoreCase) ||
                           type.Name.StartsWith("VB$", StringComparison.OrdinalIgnoreCase))
                       && (type.Attributes & System.Reflection.TypeAttributes.NotPublic) == System.Reflection.TypeAttributes.NotPublic;
        }
    }
}


Results:
Object with complete properties
Serialization
                       NetJSON: {"PersonId":123456789,"FirstName":"Linus","MiddleName":"Great","LastName":"Snoopy"}
                           Jil: {"PersonId":123456789,"LastName":"Snoopy","MiddleName":"Great","FirstName":"Linus"}
             ServiceStack.Text: {"PersonId":123456789,"LastName":"Snoopy","MiddleName":"Great","FirstName":"Linus"}
                      Json.NET: {"PersonId":123456789,"FirstName":"Linus","MiddleName":"Great","LastName":"Snoopy"}
           ASP.NET MVC Default: {"PersonId":123456789,"FirstName":"Linus","MiddleName":"Great","LastName":"Snoopy"}
Speed
                       NetJSON: 38
                           Jil: 77
             ServiceStack.Text: 89
                      Json.NET: 119
           ASP.NET MVC Default: 1022
--------------------------------------------------------------------------------
Object with null properties
Serialization
                       NetJSON: {"PersonId":123456789,"FirstName":"Linus","LastName":"Snoopy"}
                           Jil: {"PersonId":123456789,"LastName":"Snoopy","MiddleName":null,"FirstName":"Linus"}
             ServiceStack.Text: {"PersonId":123456789,"LastName":"Snoopy","MiddleName":null,"FirstName":"Linus"}
                      Json.NET: {"PersonId":123456789,"FirstName":"Linus","MiddleName":null,"LastName":"Snoopy"}
           ASP.NET MVC Default: {"PersonId":123456789,"FirstName":"Linus","MiddleName":null,"LastName":"Snoopy"}
Speed
                       NetJSON: 28
                           Jil: 84
             ServiceStack.Text: 75
                      Json.NET: 131
           ASP.NET MVC Default: 1020
--------------------------------------------------------------------------------
Object with inheritance
Serialization
                       NetJSON: {"IsActive":true,"PersonId":123456789,"FirstName":"Linus","MiddleName":"Great","LastName":"Snoopy"}
                           Jil: {"IsActive":true}
             ServiceStack.Text: {"IsActive":true}
                      Json.NET: {"IsActive":true,"PersonId":123456789,"FirstName":"Linus","MiddleName":"Great","LastName":"Snoopy"}
           ASP.NET MVC Default: {"IsActive":true,"PersonId":123456789,"FirstName":"Linus","MiddleName":"Great","LastName":"Snoopy"}
Speed
                       NetJSON: 37
                           Jil: 53
             ServiceStack.Text: 48
                      Json.NET: 148
           ASP.NET MVC Default: 1307
--------------------------------------------------------------------------------
Anonymous object with complete properties
Serialization
NetJSON Replaced with Json.NET: {"PersonId":123456789,"FirstName":"Linus","MiddleName":"Great","LastName":"Snoopy","IsActive":true}
                           Jil: {"IsActive":true,"PersonId":123456789,"LastName":"Snoopy","MiddleName":"Great","FirstName":"Linus"}
             ServiceStack.Text: {"IsActive":true,"PersonId":123456789,"LastName":"Snoopy","MiddleName":"Great","FirstName":"Linus"}
                      Json.NET: {"PersonId":123456789,"FirstName":"Linus","MiddleName":"Great","LastName":"Snoopy","IsActive":true}
           ASP.NET MVC Default: {"PersonId":123456789,"FirstName":"Linus","MiddleName":"Great","LastName":"Snoopy","IsActive":true}
Speed
NetJSON Replaced with Json.NET: 156
                           Jil: 94
             ServiceStack.Text: 111
                      Json.NET: 171
           ASP.NET MVC Default: 1702
--------------------------------------------------------------------------------
Anonymous object. Property with inheritance
Serialization
NetJSON Replaced with Json.NET: {"Greet":"Hello","Employee":{"IsActive":true,"PersonId":123456789,"FirstName":"Linus","MiddleName":"Great","LastName":"Snoopy"}}
                           Jil: {"Employee":{"IsActive":true},"Greet":"Hello"}
             ServiceStack.Text: {"Employee":{"IsActive":true},"Greet":"Hello"}
                      Json.NET: {"Greet":"Hello","Employee":{"IsActive":true,"PersonId":123456789,"FirstName":"Linus","MiddleName":"Great","LastName":"Snoopy"}}
           ASP.NET MVC Default: {"Greet":"Hello","Employee":{"IsActive":true,"PersonId":123456789,"FirstName":"Linus","MiddleName":"Great","LastName":"Snoopy"}}
Speed
NetJSON Replaced with Json.NET: 222
                           Jil: 95
             ServiceStack.Text: 103
                      Json.NET: 211
           ASP.NET MVC Default: 2200
--------------------------------------------------------------------------------


Summary:
* NetJSON is the fastest
* Jil and ServiceStack can't serialize inherited classes
* NetJSON doesn't serialize null propeties, good optimization

As for replacing NetJSON with Json.NET, NetJSON can't serialize anonymous objects yet



Happy Coding!

Tuesday, May 17, 2011

Pass complex objects from jQuery to ASP.NET MVC controller method

For some reason this doesn't work even when including traditional

$('#Complex').click(function () {

    var y = [];

    for (i = 0; i < 3; ++i) {
        var x = new Object();
        x.MemberName = "George" + i;
        x.BestSong = "Something" + i;
        x.BirthYear = 1943 + i;
        y.push(x);
    }


    var band = { BandName: 'Beatles', Members: y };


    $.ajax({
        url: '/Home/Complex/',
        type: 'POST',
        traditional: true,
        data: band,                
        dataType: 'json',
        success: function (data) {
            alert(data.BandName);
            alert(data.Members.length);
            for (i = 0; i < data.Members.length; ++i) {
                var m = data.Members[i];
                alert(m.MemberName + ' / ' + m.BestSong + ' / ' + m.BirthYear);
            }
        }
    });

});
But this one works:
$('#Complex').click(function () {

    var y = [];

    for (i = 0; i < 3; ++i) {
        var x = new Object();
        x.MemberName = "George" + i;
        x.BestSong = "Something" + i;
        x.BirthYear = 1943 + i;
        y.push(x);
    }


    var band = { BandName: 'Beatles', Members: y };


    var json = JSON.stringify(band);



    $.ajax({
        url: '/Home/Complex/',
        type: 'POST',                

        contentType: 'application/json; charset=utf-8',
        data: json,

        dataType: 'json',
        success: function (data) {
            alert(data.BandName);
            alert(data.Members.length);
            for (i = 0; i < data.Members.length; ++i) {
                var m = data.Members[i];
                alert(m.MemberName + ' / ' + m.BestSong + ' / ' + m.BirthYear);
            }
        }
    });

});
The Controller:
[HttpPost]
public JsonResult Complex(Rockband b)
{
    return Json(b);
}
The Model:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace JquerySendBatch.Models
{
    public class Rockband
    {
        public string BandName { get; set; }
        public IList<Member> Members { get; set; }
    }

    public class Member
    {
        public string MemberName { get; set; }
        public string BestSong { get; set; }
        public int BirthYear { get; set; }
    }
}

Saturday, March 5, 2011

Passing anonymous type to ASP.NET MVC's View

We could take two approaches for passing anonymous type to View. One is to use a ViewModel, another one is to use dynamic(albeit you will lose intellisense and type-safety)

Approach #1 Using a ViewModel:

If your view is expecting a model of Person type (e.g. @model IEnumerable<TestEf.Models.Person>) and you pass this to your View:

var r = (from p in db.Persons
   join q in db.Qualifieds on p.PersonId equals q.PersonId into pQ
   from l in pQ.DefaultIfEmpty()
   orderby p.Lastname
   select new { Person = p, IsQualified = l != null })
  .Skip(0).Take(10) // do the paging logic here

return View(r);

That will result to an error. The best way to handle anonymous type while maintaining strongly-typed Model is to introduce a ViewModel, so in our example here, we need to create this class on Models:

public class QualifiedStatus
{
    public Person Person { get; set; }
    public bool IsQualified { get; set; }
}


This will be the code for our controller:

var r = (from p in db.Persons
   join q in db.Qualifieds on p.PersonId equals q.PersonId into pQ
   from l in pQ.DefaultIfEmpty()
   orderby p.Lastname
   select new QualifiedStatus { Person = p, IsQualified = l != null })
  .Skip(0).Take(10) // do the paging logic here

return View(r);


And this will be the code in our View's header:

@model IEnumerable<TestEf.Models.QualifiedStatus>

Approach #2:

If we don't want to introduce ViewModels for Linq operation that results to anonymous type, we must devise a way to persist the anonymous type in a dynamic(C# 4 feature)-friendly structure. In this regard, we could use Json, as it is easy to parse-back so-to-speak(in fact, there's no need for us to parse back the string, there are many Javascript serializer/deserializer libraries that can do the heavylifting for us). And fortunately someone already written a Json deserializer that leverages C# 4's dynamic feature.


So given this:

var r = (from p in db.Persons
   join q in db.Qualifieds on p.PersonId equals q.PersonId into pQ
   from l in pQ.DefaultIfEmpty()
   orderby p.Lastname
   select new { Person = p, IsQualified = l != null })
  .Skip(0).Take(10) // do the paging logic here


We must be able to pass it directly to MVC's view, to make our API simple, we could make an extension method that allows serializing an object to Json string:

return View((object)r.JsSerialize());

By the way, we cannot pass the string result of JsSerialize directly to View, otherwise the View string overload function will be the one being invoked in our code, and that string is a url, it will be an error to pass Json as url :-) So in our code, we must cast string to object

On the View side of things, we put this on View's header(actually this is optional, dynamic is the default):
@model dynamic

And put this code:

@{ dynamic r = ((string)Model).JsDeserialize(); }


We could now code our View in the usual way, albeit without the type-safety and without the comfort of intellisense(if lacking this is not enough to scare you, I don't know what could :p bottomline, use strongly-typed model, use ViewModels):

@foreach (var item in r) {
 <tr>
  <td>
   @item.Person.Lastname
  </td>
  <td>
   @item.Person.Firstname
  </td>
  <td>
   @item.Person.FavoriteNumber
  </td>
  <td>
   <input type="checkbox" disabled="disabled" @(item.IsQualified ? "checked" : "") />
  </td>
 </tr>
}


See things in action: http://code.google.com/p/sample-poc-for-passing-anonymous-type-to-aspnet-mvc-view/downloads/list

Just change Web.config's connection string:
connectionString="Data Source=C:\Users\Michael\Documents\Visual Studio 2010\Projects\TestEf\TestEf\Database\Ent.sqlite; Version=3"


If you want to use Sql Server instead, change the connection's providerName to System.Data.SqlClient and the connectionString accordingly, here's the DDL:
create table Person
(
PersonId bigint identity(1,1) not null primary key,
Lastname varchar(50) not null,
Firstname varchar(50) not null,
FavoriteNumber int not null
);

create table Qualified
(
QualifiedId bigint identity(1,1) not null primary key,
PersonId bigint references Person(PersonId) not null
);

insert into Person(Lastname, Firstname, FavoriteNumber) values
('Harrison','Jorg', 7),
('Lennon','Jan', 9),
('McCartney','Pol',8);


insert into Qualified(PersonId) values
(1),(3);

UPDATE April 17, 2011

Updated to Entity Framework 4.1. And also, the code is not dependent on installing Sqlite.NET provider anymore, uses installer-less Sqlite .NET provider, just add the System.Data.SQLite.DLL and System.Data.SQLite.Linq.dll directly; necessary Web.config entry for uninstaller-less Sqlite .NET provider is also added to Web.config
http://code.google.com/p/sample-poc-for-passing-anonymous-type-to-aspnet-mvc-view/downloads/detail?name=TestEf-4.1.zip&can=2&q=

Friday, March 4, 2011

ASP.NET MVC + jQuery: The new whole nine yards for Microsoft app(web) development

What is the easiest way to validate a model without requiring a round-trip to server? The answer folks is to use the venerable jQuery and its most compatible companion, ASP.NET MVC.

An aside, did you know that your inputs are already a model-material just by conforming to certain naming conventions for your control's name? And did you know also that the easiest way to achieve this is by using ASP.NET MVC's Html helpers and not coding the HTML manually? :-) I digress.

Sorry folks, jQuery and ASP.NET is not a match made in heaven, the server controls' id in ASP.NET are mangled to no end for it to be conveniently accessible from jQuery's simplified model-pushing (heheh I made up the term, the actual jQuery function is .serialize() ) mechanism. In short, use ASP.NET MVC, strongly recommended :-)


So here's our model:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;

namespace MvcApplication3.Models
{
    public class Person
    {
        [Required(ErrorMessage="Last Name required")]
        public string Lastname { get; set; }

        [Required(ErrorMessage="First Name required")]
        public string Firstname { get; set; }

        [Required(ErrorMessage="Age Required")]
        [Range(0,120, ErrorMessage="Age must be between 0 and 120")]
        public int Age { get; set; }


        // sourced here: http://www.regular-expressions.info/email.html
        [Required(ErrorMessage="Email required")]
        [RegularExpression(@"[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?",
            ErrorMessage="Not a valid email")]
        public string Email { get; set; }
    
    }
}

And this is our view, coupled with client-side validation awesomeness (pertinent lines: 19, 20, 24, 78, 81, 89):

@model MvcApplication3.Models.Person

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <title>Index</title>
</head>
<body>
    <script src="@Url.Content("~/Scripts/jquery-1.4.4.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>

    @* add these two scripts *@
    <script src="@Url.Content("~/Scripts/MicrosoftAjax.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/MicrosoftMvcValidation.js")" type="text/javascript"></script>


    @* add this too *@
    @{ Html.EnableClientValidation(); }

    @using (Html.BeginForm(null, null, FormMethod.Post, new { id = "See" } ))
    {
        @Html.ValidationSummary(true)
        <fieldset>
            <legend>Person</legend>
    
            <div class="editor-label">
                @Html.LabelFor(model => model.Lastname)
            </div>
            <div class="editor-field">
                @Html.EditorFor(model => model.Lastname)
                @Html.ValidationMessageFor(model => model.Lastname)
            </div>
    
            <div class="editor-label">
                @Html.LabelFor(model => model.Firstname)
            </div>
            <div class="editor-field">
                @Html.EditorFor(model => model.Firstname)
                @Html.ValidationMessageFor(model => model.Firstname)
            </div>
    
            <div class="editor-label">
                @Html.LabelFor(model => model.Age)
            </div>
            <div class="editor-field">
                @Html.EditorFor(model => model.Age)
                @Html.ValidationMessageFor(model => model.Age)
            </div>
    
            <div class="editor-label">
                @Html.LabelFor(model => model.Email)
            </div>
            <div class="editor-field">
                @Html.EditorFor(model => model.Email)
                @Html.ValidationMessageFor(model => model.Email)
            </div>
    
            <p>
                <input type="submit" id="submitter" value="Create" />
            </p>
        </fieldset>
    }
    


    <script type="text/javascript">


        $("#submitter").click(function (event) {

            // this prevent the roundtrip to server            
            event.preventDefault();

            // this prevent ajax-sending if there's still an invalid logic on model
            if (!$("#See").validate().form()) return;

            $.ajax(
                {
                    url: '/Home/Create/',
                    type: 'POST',
                    dataType: 'json',
                    // .serialize is a free beer
                    data: $("#See").serialize(),
                    // whereas if there's no .serialize, you have to send individual inputs manually
                    // i.e.
                    // data: { Lastname : $("#Lastname").val(), Firstname : $("#Firstname").val() },

                    success: function (r) {
                        alert(r.Message);
                    }
                });

        });


        


    </script>
</body>
</html>


And this is the controller (pertinent lines: 24, 33, 41):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MvcApplication3.Models;

namespace MvcApplication3.Controllers
{
    public class HomeController : Controller
    {
        //
        // GET: /Home/

        public ActionResult Index()
        {
            return View();
        }


        public JsonResult Create(Person p)
        {

            if (ModelState.IsValid)
            {
                try
                {
                    // use your ORM of choice here to persist your model 

                    // return feedback to jQuery
                    return Json(new { Message = "Your name is " + p.Lastname + ", " + p.Firstname });
                }
                catch (Exception ex)
                {
                    return Json(new { Message = ex.Message });
                }
                
            }
            else
                // alert the user to tell the developer of an overlooked code
                return Json(new { Message = "Please contact the developer to enable client-side validation" });
        }

    }
}



Get the actual code here: http://code.google.com/p/sample-poc-for-jquery-aspnet-mvc-validation/downloads/list

Happy validating folks! :-)