Click here to Skip to main content
15,946,342 members
Articles / Programming Languages / C# 4.0

JSON API

Rate me:
Please Sign up or sign in to vote.
4.90/5 (101 votes)
6 Dec 2011CPOL11 min read 322.4K   7.2K   243   100
A simple look at how to share the JSON API to Web and Desktop.

Table of Contents

Introduction

Hello all, long time since you heard from me, huh? Well, I have not been dormant, I am actually working on a pretty large Open Source tool for developers; it is kind of an organizational tool which is eating up all my time. In fact, it's not just my time it is eating, it is also eating up a fair chunk of fellow CodeProjecter Pete O'Hanlon's time, who foolishly, um nicely, volunteered to help me out with creating this tool.

The tool is coming along nicely, it is actually an ASP MVC 3 web site, and some of the stuff that I have done in it is pretty useful I think, so I will be creating a few articles about some of the stuff it uses, and I will of course be writing up an article or two about how it works.

So what is this article all about? Well, one thing that the tool that Pete O'Hanlon and I are working on needs to do is expose a JSON (Java Script Object Notation) API that people can use. This same API also needs to be callable from within a standard .NET application. That is really what this article is all about. It shows one way (there are loads of ways, this is just one way) of how you might go about exposing a JSON API that is callable both from the web and via web based APIs from a standard desktop .NET client app, with the minimum of fuss, no config, other than the standard one you get with .NET installed.

It does however make use of ASP MVC 3 so that is assumed, as is .NET 4 and VS2010.

Available Frameworks

At first glance, you might think of the problem as being quite suited to some sort of WCF service that could be called by both JavaScript and standard .NET code. So that really gives us two possibilities really:

RESTFul WCF

This came out in .NET 3.5 SP1 and allowed to write a normal WCF service that you could attribute up with special web attributes, something like:

C#
[ServiceContract]
public interface ICalculator
{
    [OperationContract]
    [WebInvoke(UriTemplate = "add?x={x}&y={y}")]
    long Add(long x, long y);

    [OperationContract]
    [WebInvoke(UriTemplate = "sub?x={x}&y={y}")]
    long Subtract(long x, long y);

    [OperationContract]
    [WebInvoke(UriTemplate = "mult?x={x}&y={y}")]
    long Multiply(long x, long y);

    [OperationContract]
    [WebInvoke(UriTemplate = "div?x={x}&y={y}")]
    long Divide(long x, long y);

    [OperationContract]
    [WebGet(UriTemplate = "hello?name={name}")]
    string SayHello(string name);
}

public class CalcService : ICalculator
{
    public long Add(long x, long y)
    {
        return x + y;
    }

    public long Subtract(long x, long y)
    {
        return x - y;
    }

    public long Multiply(long x, long y)
    {
        return x * y;
    }

    public long Divide(long x, long y)
    {
        return x / y;
    }

    public string SayHello(string name)
    {
        return "Hello " + name;
    }
}

class Program
{
    static void Main(string[] args)
    {
        Uri baseAddress = new Uri("http://localhost:8000/");

        WebServiceHost svcHost = 
           new WebServiceHost(typeof(CalcService), baseAddress);

        try
        {
            svcHost.Open();

            Console.WriteLine("Service is running");
            Console.WriteLine("Press enter to quit...");
            Console.ReadLine();

            svcHost.Close();
        }
        catch (CommunicationException cex)
        {
            Console.WriteLine("An exception occurred: {0}", cex.Message);
            svcHost.Abort();
        }
    }
}

But you also need to use a new WebServiceHost to host this service, and you also have to use a specific port for the service.

I actually wrote a pretty substantial article on this a while back; if you are interested, you can read about that at http://www.codeproject.com/KB/smart/GeoPlaces.aspx.

WCF Web API

The second option available to your average .NET developer is to use the WCF Web API which is being pushed by one of my favourite chaps at Microsoft, Mr. Glenn Block. It can be kind of seen as a progression of the RESTful WCF stuff. It is quite new, and not part of the .NET Framework (yet).

Here is a small example of how you might configure a service using the WCF Web APIs, where this would typically reside in a web site (such as an ASP MVC one):

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;
using System.Net;
using System.Net.Http;
using Microsoft.ApplicationServer.Http.Dispatcher;


namespace RESTFul_WCF
{
    /// <summary>
    /// Demo service using the new WEB WCF APIs
    /// </summary>
    [ServiceContract]
    public class EFResource
    {
        int dummyCounter = 0;

        [WebGet(UriTemplate = "", 
            RequestFormat = WebMessageFormat.Json, 
            ResponseFormat = WebMessageFormat.Json)]
        public IQueryable<Models.Author> Get()
        {
            //dummy code, this would hit database normally
            List<Models.Author> authors = new List<Models.Author>();
            for (int i = 0; i < 5; i++)
            {
              authors.Add(new Models.Author(
                i,string.Format("Some Author_{0}", i.ToString())));
            }

            return authors.AsQueryable();
        }

        [WebInvoke(UriTemplate = "", Method = "POST", 
            RequestFormat = WebMessageFormat.Json, 
            ResponseFormat = WebMessageFormat.Json)]
        public Models.Author Post(Models.Author author)
        {
            if (author == null)
            {
                throw new HttpResponseException(HttpStatusCode.BadRequest);
            }

            //dummy code, this would hit database normally
            author.Id = dummyCounter++;
            return author;
        }
    }
}

Where you might have this in your Global.asax.cs file (this is ASP MVC specific, there will be an equivalent setting if not using ASP MVC, I am sure):

C#
private void Application_Start(object sender, EventArgs e)
{
    // setting up contacts services
    RouteTable.Routes.MapServiceRoute<EFResource>("ef");
}

Note: One of the readers of the article actually pointed out that you can get the WCF Web APIs to work nicely with ASP MVC and its routing goodness. Although this is not in the attached demo app, this is how you would do it.

C#
public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.Add(new ServiceRoute("api/resource", 
               new HttpServiceHostFactory(), typeof(EFResource)));

    routes.MapRoute(
        "Default", // Route name
        "{controller}/{action}/{id}", // URL with parameters
        new { controller = "Home", action = "Index", 
              id = UrlParameter.Optional } // Parameter defaults
    );
}

And that is all there is to the service part (OK, there is a very small bit of config, but way less than the old versions of WCF).

So what about a consuming client? What would that look like? Well, it looks like this, this is it in its entirety, no config needed at all:

C#
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Xml.Serialization;
using Microsoft.ApplicationServer.Http;

using Models;

namespace RestFul_Test
{
    class Program
    {

        private enum MimeFormat { JSON, Xml };

        static void Main(string[] args)
        {
            string uri = "http://localhost:8300/ef";

            //GET Using JSON
            HttpClient client = GetClient(uri, MimeFormat.JSON);
            var response = client.Get(uri);

            Console.WriteLine("=========GET==============");
            foreach (Author author in response.Content.ReadAs<List<Author>>())
            {
                LogAuthorResultToConsole(author);
            }


            //POST using JSON
            Console.WriteLine("\r\n");
            Console.WriteLine("=========POST==============");


            var newAuthor = new Author { Name = "Xml Barber" };
            var request = new HttpRequestMessage(HttpMethod.Post, new Uri(uri));
            request.Content = new ObjectContent<Author>(newAuthor, "application/json");
            request.Headers.Accept.Add(
              new MediaTypeWithQualityHeaderValue("application/json"));
            response = client.Send(request);
            var receivedAuthor = response.Content.ReadAs<Author>();
            LogAuthorResultToConsole(receivedAuthor);
            Console.ReadLine();
        }

        private static void LogAuthorResultToConsole(Author author)
        {
            Console.WriteLine(string.Format(CultureInfo.InvariantCulture,
                "\r\n Author Name: {0}, Author Id: {1}", author.Name, author.Id));
            
        }

        private static HttpClient GetClient(string uri, MimeFormat format)
        {
            var client = new HttpClient(new Uri(uri));
            switch (format)
            {
                case MimeFormat.Xml:
                    client.DefaultRequestHeaders.Accept.Add(
                        new MediaTypeWithQualityHeaderValue("application/xml"));
                    break;
                case MimeFormat.JSON:
                    client.DefaultRequestHeaders.Accept.Add(
                        new MediaTypeWithQualityHeaderValue("application/json"));
                    break;
            }
            return client;
        }
    }
}

I really like this, but it requires the use of quite a few extra DLLs, even the client needs a few extra DLLs.

Note: I have included a small demo app of this at the top of the article, as I thought it may be of interest to some folk out there.

Problems With These Frameworks

There are several problems which need to be considered, which I will go into one by one below:

RESTFul WCF

Using RESTful WCF (which was available in .NET 3.5 SP1) would have worked, but there are several hurdles:

  • It needs to use an extra port. Now since this is part of a web site, I am already using one port for the actual web site. Using the RESTful WCF stuff, I would now need to maintain another port.
  • The .NET client needs WCF DLLs as well as some special REST WCF DLLs.
  • Need to create a client side proxy.
  • Maintain an App.Config for all the WCF configuration client and server.
  • Use of eTags if doing things properly.
  • Maybe have to get into MessageInspector to change ContextType for HttpRequest; JSON is supported, so that would have been OK, but you see what I mean?

WCF Web API

I am a massive fan of the WCF Web API that Glenn Block and his team are working on; however, this too has a few issues for me, namely:

  • The thing that put me off using the WCF Web API was the sheer amount of extra DLLs that were needed on the server; I was quite shocked when I installed the nuget package for the WCF Web API.
  • Even the client needed extra DLLs (HttpClient and others).

The good thing is there is zero config needed, it just works. OK, you do need some of the extra WCF Contrib stuff to make it work really well with DataContract serialization, but that's tolerable.

That said, there is quite good support for the WCF Web APIs in ASP MVC, which you can read about here: http://wcf.codeplex.com/wikipage?title=Getting%20started:%20Building%20a%20simple%20web%20api. As I say, I really liked the WCF Web APIs and if it was not for all the DLLs that were needed, I would have probably gone with this.

A Possible Solution (worked for me at least)

So now that I have outlined what I found objectionable with the existing solutions, what other choice did I really have left available to me? What I really wanted was no extra port to maintain/open, no configuration, no extra DLLs, the code should work when called via JavaScript, say via jQuery and from a standard .NET client app using standard .NET web APIs.

Use ASP MVC

The logical conclusion I came to was to just use ASP MVC itself. This is good for a number of reasons, namely:

  • ASP MVC controllers can be called from any JavaScript including jQuery obviously
  • The ASP MVC routing is already there
  • The web site is already available on a port, no need to make another one available
  • ASP MVC controller is just a URL, as such can be called by standard .NET web APIs with ease
  • ASP MVC 3 (which is what I am using) actually comes pre-canned with certain ValueProviderFactory objects, of which JsonValueProviderFactory is one

Basically, ASP MVC does lots of work for you.

So what would this typically look like? Well, let's have a look, shall we?

Here is a full ASP MVC 3 controller that accepts JSON and also returns JSON. There are three different actions there, which we will talk about later within this article.

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Common;
using System.IO;
using System.Text;
using System.Runtime.Serialization.Json;
using CodeStash.Filters;
using System.Json;
using RestfulWebSite.ModelBinders;

namespace RestfulWebSite.Controllers
{
    /// <summary>
    /// This controller shows 3 different methods of accepting JSON values
    /// 1. Accepts JSON from JavaScript in the browser,
    /// and also from .NET client app using standard web APIs
    /// 2. Accepts JSON from JavaScript in the browser
    /// 3. Uses JSONValue API to accept JSON as dynamic object
    /// </summary>
    public class RestController : Controller
    {

        /// <summary>
        /// Uses special ActionFilter to convert desktop JSON data
        /// into acceptable action value. Returns standard JSON result
        /// </summary>
        [JSONFilter(Param = "person", RootType = typeof(Person))]
        public ActionResult Index(Person person)
        {
            int age = person.Age;

            List<Person> people = new List<Person>();
            for (int i = 0; i < 5; i++)
            {
                string s = string.Format("{0}_{1}", "hardCoded", i.ToString());
                people.Add(new Person(i, s, new Parent(1, "parent")));
            }
            return Json(people);
        }

        /// <summary>
        /// Accepts standard JSON, from JavaScript call. 
        /// Returns standard JSON result
        /// </summary>
        public ActionResult NoFilterJSON(Person person)
        {
            int age = person.Age;

            List<Person> people = new List<Person>();
            for (int i = 0; i < 5; i++)
            {
                string s = string.Format("{0}_{1}", "hardCoded", i.ToString());
                people.Add(new Person(i, s, new Parent(1, "parent")));
            }
            return Json(people);
        }

        /// <summary>
        /// Uses JSONValue to convert to dynamic object. 
        /// Returns standard JSON result
        /// </summary>
        public ActionResult JsonValue(

            [DynamicJson(typeof(Person))]
            JsonValue person
        )
        {
            var x = person.AsDynamic();
            int age = x.Age;

            List<Person> people = new List<Person>();
            for (int i = 0; i < 5; i++)
            {
                string s = string.Format("{0}_{1}", "hardCoded", i.ToString());
                people.Add(new Person(i, s, new Parent(1, "parent")));
            }
            return Json(people);
        }
    }
}

Customising MVC Behaviour

For the first action shown above, one thing that must be done when calling this ASP MVC controller code directly from a standard .NET client (at least the client code associated with this article) is to use a custom ActionFilterAttribute to supply the controller action model data.

This is shown below:

C#
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Json;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Ajax;
using System.Xml.Linq;
using System.Xml.Serialization;
using System.Runtime.Serialization;
using System.Text;
using System.Json;

namespace CodeStash.Filters
{
    public class JSONFilter : ActionFilterAttribute
    {
        public string Param { get; set; }

        public Type RootType { get; set; }

        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            try
            {
                string json = filterContext.HttpContext.Request.Form[Param];
                string contentType = filterContext.HttpContext.Request.ContentType;
                
                switch (contentType)
                {
                    //Which is what you get if you use the WebClient in your code.
                    //Which is what the .NET client code is using
                    case "application/x-www-form-urlencoded":
                        if (json == "[]" || json == "\",\"" || String.IsNullOrEmpty(json))
                        {
                            filterContext.ActionParameters[Param] = null;
                        }
                        else
                        {
                            using (var ms = new MemoryStream(Encoding.Unicode.GetBytes(json)))
                            {
                                filterContext.ActionParameters[Param] = 
                                    new DataContractJsonSerializer(RootType).ReadObject(ms);
                            }
                        }
                        break;
                    case "application/json":
                        //allow standard Controller/ASP MVC JSONValueProvider to do its work
                        //we can't do a better job than it, so let it deal with it
                        return;
                    default:
                        filterContext.ActionParameters[Param] = null;
                        break;
                }
            }
            catch
            {
                filterContext.ActionParameters[Param] = null;
            }
        }

    }
}

This code runs, thanks to the use of this specialized ActionFilterAttribute which we use as follows in the demo app attached:

C#
[JSONFilter(Param = "person", RootType = typeof(Person))]
public ActionResult Index(Person person)
{
    .....
}

Some of the more eagle eyed amongst you will notice that we do two things in there:

  1. If the HTTP ContentType is "application/x-www-form-urlencoded", we try and use a DataContractJsonSerializer to provide the data for the controller action model. Why do we do that? Well, in the attached demo code, the .NET client app uses the WebClient web class which makes use of the "application/x-www-form-urlencoded" HTTP ContentType. It does not allow you to change that, which is fair enough as the WebClient exposes a values collection which could accept anything, so it must use this ContentType. You will see more on this later on in the article.
  2. If the HTTP ContentType is "application/json", we allow ASP MVC to supply the JSON data as it has inbuilt support for this, thanks to the inbuilt JsonValueProviderFactory. It does a great job, so let it do it.

JSONValue

Before we go onto to look at the .NET client and the jQuery demos, I just wanted to take a slight diversion, which is to talk about an interesting API which has come out of the WCF Web API work but is also available as a standalone nuget package.

This standalone package is called JSONValue and is available by adding a Package Reference from within Visual Studio.

The third of the demo controller actions uses JSONValue.

What JSONValue brings to the table is that it has certain wrappers/converters around JavaScript objects, it also has dynamic support. I think one of the best ways is to see an example of this.

So as we extended ASP MVC before with a custom ActionFilterAttribute, ASP MVC also supports extensibility via the use of specialized model binders, so let's have a look at an example of that, shall we?

So in our controller we have this, see how it simply accepts a JsonValue and in the actual controllers action, it is able to use the AsDynamic() method to get the correct property values for the type that was received.

C#
public ActionResult JsonValue(

    [DynamicJson(typeof(Person))]
    JsonValue person
)
{

    var x = person.AsDynamic();
    int age = x.Age;
}

Where it can be seen that I am using a special DynamicJsonAttribute which looks like this:

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace RestfulWebSite.ModelBinders
{
    public class DynamicJsonAttribute : CustomModelBinderAttribute
    {
        private Type rootType;

        public DynamicJsonAttribute(Type rootType)
        {
            this.rootType = rootType;
        }

        public override IModelBinder GetBinder()
        {
            return new DynamicJsonBinder(rootType);
        }
    }
}

This attribute is responsible for creating the data that is passed as the model value for the attributed object. It does this by using the internal instance of a DynamicJsonBinder class, which looks like this:

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.IO;
using System.Json;
using System.Runtime.Serialization.Json;
using System.Text;

namespace RestfulWebSite.ModelBinders
{
    public class DynamicJsonBinder : IModelBinder
    {
        private Type rootType;
        public DynamicJsonBinder(Type rootType)
        {
            this.rootType = rootType;
        }

        public object BindModel(ControllerContext controllerContext, 
        ModelBindingContext bindingContext)
        {
            try
            {
                var inpStream = controllerContext.HttpContext.Request.InputStream;
                inpStream.Seek(0, SeekOrigin.Begin);
                string bodyText;

                using (StreamReader reader = new StreamReader(
                    controllerContext.HttpContext.Request.InputStream))
                {
                    bodyText = reader.ReadToEnd();
                }

                string contentType = controllerContext.HttpContext.Request.ContentType;
                object result=null;

                switch(contentType)
                {
                    case "application/json":
                    case "application/json; charset=UTF-8":
                        if (!String.IsNullOrEmpty(bodyText))
                        {
                            result= JsonValue.Parse(bodyText);
                        }
                        break;
                    default:
                        result= null;
                        break;
  
                }
                return result;

            }
            catch(Exception ex)
            {
                return null;
            }
        }
    }
}

This may also sound a bit much for now, but hopefully when you see the calling code, it will all make sense. Let's proceed to look at that now, shall we?

Demo: Browser Support

All three of the demo controller actions accept JSON produced by the following three pieces of jQuery, which are all the same actually, apart from the actual ASP MVC controller action they call:

JavaScript
$(document).ready(function () {
    var person = { Age: 1, Name: "person1" };

    $("#btnActionFilter").click(function () {
        $.ajax({
            url: "/Rest/Index",
            type: "POST",
            dataType: "json",
            contentType: 'application/json',
            data: JSON.stringify(person),
            success: function (response) {
                alert(response);
                $("#personContainer").empty()
                $("#personTemplate").tmpl(response).appendTo("#personContainer");
            }
        });
    });

    $("#btnNoFilterJSON").click(function () {
        $.ajax({
            url: "/Rest/NoFilterJSON",
            type: "POST",
            dataType: "json",
            contentType: 'application/json',
            data: JSON.stringify(person),
            success: function (response) {
                alert(response);
                $("#personContainer").empty()
                $("#personTemplate").tmpl(response).appendTo("#personContainer");
            }
        });
    });

    $("#btnJSONValue").click(function () {
        $.ajax({
            url: "/Rest/JsonValue",
            type: "POST",
            dataType: "json",
            contentType: 'application/json',
            data: JSON.stringify(person),
            success: function (response) {
                alert(response);
                $("#personContainer").empty()
                $("#personTemplate").tmpl(response).appendTo("#personContainer");
            }
        });
    });
});

One thing that I have also included (just for kicks) is the use of the jQuery Templates that allow you to do repeated bit of HTML with placeholders where the placeholders are filled in using JSON objects.

Here is a jQuery Template inside the demo code:

HTML
<div>
    <h1>Results</h1>
    <div id="personContainer"></div>
</div>
    
<script id="personTemplate" type="text/x-jQuery-tmpl">
    <div>
        <p><strong>Person</strong>
        <br/>
        Age:${Age}
        <br/>
        Name:${Name}
        <br/>
        TimeStamp:${TimeStamp}
        <br/>
    </div>
</script>

This is basically what is used to show the results of calling the action methods on the controllers. Nice, no? jQuery Template has much more to offer, it is worth digging deeper if you have the time.

Demo: Desktop .NET Client Support

All that remains is for me to show you how to call the ASP MVC controller using a standard .NET client which uses standard .NET web APIs.

For the demo app, the client code looks like this (where we are calling the REST controller's Index action from this code).

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Collections.Specialized;
using System.Web.Script.Serialization;
using Common;
using System.Runtime.Serialization.Json;
using System.Runtime.Serialization;
using System.IO;
using System.Xml;

namespace ConsoleApplication1
{
    class Program
    {
        internal static DataContractJsonSerializer jss;
        private static WebClient client = new WebClient();
        private static NameValueCollection values = 
                new System.Collections.Specialized.NameValueCollection();

        static void Main(string[] args)
        {
            //Use ModelBinding in WebSite to resolve types to Controller Actions
            Person pers = new Person(1, "name", null);
            AddValue(values,"person", pers);

            //NOTE : WebClient doesn't allow you to set ContentType header to "application/json" when using the UploadValues() method
            //       which is a shame as it would be very nice. But since the WebClient allows
            //       you to add anything to its collection of values, it makes sense to restrict the
            //       WebClient ContentType header to "application/x-www-form-urlencoded" (the default)
            //       as you can't really know what data you are sending, could be anything being sent up
            //       in one go, so what ContentType could be reasonably provided except 
            //       "application/x-www-form-urlencoded"
            //       Also the WebClient is kind of a dumbed down HttpRequest, 
            //       so its more limited, but also more convenient

            Byte[] results = client.UploadValues("http://localhost:8300/Rest/Index", values);
            //get results
            List<Person> people = GetValue<List<Person>>(results);

            Console.ReadLine();
        }

        internal static T GetValue<T>(Byte[] results) where T : class
        {
            using (MemoryStream ms = new MemoryStream(results))
            {
                jss = new DataContractJsonSerializer(typeof(T));
                return (T)jss.ReadObject(ms);
            }
        }

        internal static void AddValue(NameValueCollection values, string key, object value)
        {
            jss = new DataContractJsonSerializer(value.GetType());
            using (MemoryStream ms = new MemoryStream())
            {
                jss.WriteObject(ms, value);
                string json = Encoding.UTF8.GetString(ms.ToArray());
                values.Add(key, json);
            }
        }
    }
}

It is all pretty easy stuff. We just make use of WebClient along with DataContractJsonSerializer to form our NameValueCollection and make a request to the web site. All standard stuff, but it did take me a while to get all the pieces to work nicely together, so I thought this article may benefit others.

As I have been banging on, this needs just standard .NET classes, zero config, no port specific to the call, just the web site URL, and we do our own very lean serialization. Actually, using JSON as a serialization format is pretty good, as it is very lightweight when compared to other forms of serialization, and it is also very human readable, something SOAP formatting suffers from a bit, I feel.

More

Since I 1st wrote this article, a codeproject user "Simonotti Paolo" was also doing what I am doing, we kind of both had a nirvana moment, Whoa we are not alone. Anyway Simonotti showed a way in which you can use the ContentType application/json and the WebClient, and he has kindly agreed for me to include it here. So if you really want to use the ContentType application/json. Here is how

Instead of my Desktop Client code you would have something like this

C#
public static class JsonExtensionMethods
{
    public static T JsonInvoke<T>(this WebClient client, string methodUri, object argument) where T : class
    {
        T res = default(T);
        client.Headers.Add("Content-Type", "application/json");
        byte[] jsonRequest = Encoding.UTF8.GetBytes(argument.ToJson());
        byte[] jsonResponse = client.UploadData(methodUri, jsonRequest);
        res = jsonResponse.JsonDeserializeTo<T>();
        return res;
    }
 
    public static string ToJson(this object obj)
    {
        JavaScriptSerializer jss = new JavaScriptSerializer();
        return jss.Serialize(obj);
    }
 
    public static T JsonDeserializeTo<T>(this byte[] bytes) where T:class
    {
        T res = null;
        DataContractJsonSerializer jss = new DataContractJsonSerializer(typeof(T));
        using (MemoryStream ms = new MemoryStream(bytes))
        {
            res=(T)jss.ReadObject(ms);
        }
        return res;
    }
}

Where you would use it in your desktop app like this

C#
string uri="http://localhost:8300/Rest/NoFilterJSON";
 
Person pers1 = new Person(1, "àèìòù", null);
Person pers2 = new Person(2, "àèìòù2", null);
 
Dictionary<string, object> values = new Dictionary<string, object>()
{
    { "person", pers1 },
    { "person2", pers2 }
};
 
//with dictionary
List<Person> people1 = client.JsonInvoke<List<Person>>(uri, values);
 
//with anonymous type
List<Person> people2 = client.JsonInvoke<List<Person>>(uri, new { person=pers1, person2=pers2 } );

I feel both approaches have their merits, I will let you choose, you have the tools, have fun

That's It

Anyway that is all I wanted to say for now. I realise this is not my normal WPF type article, but I think it is still useful and it shows you how to create some code that is web/desktop callable with zero config, and requires very little code.

If you liked the article, spare some time to give me a vote/comment, it would be appreciated.

License

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


Written By
Software Developer (Senior)
United Kingdom United Kingdom
I currently hold the following qualifications (amongst others, I also studied Music Technology and Electronics, for my sins)

- MSc (Passed with distinctions), in Information Technology for E-Commerce
- BSc Hons (1st class) in Computer Science & Artificial Intelligence

Both of these at Sussex University UK.

Award(s)

I am lucky enough to have won a few awards for Zany Crazy code articles over the years

  • Microsoft C# MVP 2016
  • Codeproject MVP 2016
  • Microsoft C# MVP 2015
  • Codeproject MVP 2015
  • Microsoft C# MVP 2014
  • Codeproject MVP 2014
  • Microsoft C# MVP 2013
  • Codeproject MVP 2013
  • Microsoft C# MVP 2012
  • Codeproject MVP 2012
  • Microsoft C# MVP 2011
  • Codeproject MVP 2011
  • Microsoft C# MVP 2010
  • Codeproject MVP 2010
  • Microsoft C# MVP 2009
  • Codeproject MVP 2009
  • Microsoft C# MVP 2008
  • Codeproject MVP 2008
  • And numerous codeproject awards which you can see over at my blog

Comments and Discussions

 
QuestionSwagger and Jason Pin
Beryl Duff25-Oct-18 1:11
Beryl Duff25-Oct-18 1:11 
QuestionExcellent. Thanks! Pin
kartalyildirim3-Mar-12 23:23
kartalyildirim3-Mar-12 23:23 
QuestionAuthentication alternatives Pin
MedvedevS28-Dec-11 1:59
MedvedevS28-Dec-11 1:59 
AnswerRe: Authentication alternatives Pin
Sacha Barber28-Dec-11 9:55
Sacha Barber28-Dec-11 9:55 
QuestionPossible deployment problem + solution Pin
MedvedevS23-Dec-11 2:51
MedvedevS23-Dec-11 2:51 
AnswerRe: Possible deployment problem + solution Pin
Sacha Barber23-Dec-11 22:24
Sacha Barber23-Dec-11 22:24 
GeneralMy vote of 5 Pin
Uday P.Singh7-Dec-11 4:02
Uday P.Singh7-Dec-11 4:02 
GeneralRe: My vote of 5 Pin
Sacha Barber7-Dec-11 4:24
Sacha Barber7-Dec-11 4:24 
QuestionMy vote of 4 Pin
shaheen_mix6-Dec-11 23:29
shaheen_mix6-Dec-11 23:29 
AnswerRe: My vote of 4 Pin
Sacha Barber7-Dec-11 0:03
Sacha Barber7-Dec-11 0:03 
GeneralMy vote of 5 Pin
fredatcodeproject30-Nov-11 6:38
professionalfredatcodeproject30-Nov-11 6:38 
GeneralRe: My vote of 5 Pin
Sacha Barber6-Dec-11 9:43
Sacha Barber6-Dec-11 9:43 
GeneralMy vote of 5 Pin
Monjurul Habib29-Nov-11 20:19
professionalMonjurul Habib29-Nov-11 20:19 
GeneralRe: My vote of 5 Pin
Sacha Barber6-Dec-11 9:29
Sacha Barber6-Dec-11 9:29 
GeneralMy Vote of 5 Pin
RaviRanjanKr29-Nov-11 9:37
professionalRaviRanjanKr29-Nov-11 9:37 
GeneralRe: My Vote of 5 Pin
Sacha Barber29-Nov-11 20:01
Sacha Barber29-Nov-11 20:01 
QuestionI'm not alone Pin
Simonotti Paolo29-Nov-11 0:16
Simonotti Paolo29-Nov-11 0:16 
AnswerRe: I'm not alone Pin
Sacha Barber29-Nov-11 1:02
Sacha Barber29-Nov-11 1:02 
AnswerRe: I'm not alone Pin
Sacha Barber29-Nov-11 1:04
Sacha Barber29-Nov-11 1:04 
GeneralJavascriptSerializer Pin
Simonotti Paolo29-Nov-11 6:10
Simonotti Paolo29-Nov-11 6:10 
GeneralRe: JavascriptSerializer Pin
Sacha Barber29-Nov-11 7:05
Sacha Barber29-Nov-11 7:05 
GeneralRe: JavascriptSerializer Pin
Simonotti Paolo29-Nov-11 21:18
Simonotti Paolo29-Nov-11 21:18 
GeneralRe: JavascriptSerializer Pin
Sacha Barber6-Dec-11 9:29
Sacha Barber6-Dec-11 9:29 
GeneralRe: JavascriptSerializer Pin
Sacha Barber7-Dec-11 0:05
Sacha Barber7-Dec-11 0:05 
GeneralRe: JavascriptSerializer Pin
MedvedevS28-Dec-11 2:01
MedvedevS28-Dec-11 2:01 

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

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