Chris's coding blog

Silverlight JSON WebClient wrapper/helper

June 06, 2010

This is a small class that uses the NewtonSoft JSON.NET library to make JSON requests to a URL. It handles both requests that require parameters, and those that just return results (for example a GetProducts() call).

I chose NewtonSoft over the default Microsoft JSON deserializer as it handles nested objects and doesn’t require attributes on your domain objects.

The class probably needs a few tweaks to be completely production-ready.

/// <summary>
/// Builds and executes a JSON request to an ASP.NET web service or MVC controller method.
/// </summary>
/// <typeparam name="T">Use string if the request does not return any type.</typeparam>
public class JsonRequest<T>
{
private Action<T> _completeAction;
/// <summary>
/// A container for the WebRequest async calls.
/// </summary>
private class State
{
public HttpWebRequest Request { get; set; }
public Dictionary<string, string> Parameters { get; set; }
}
/// <summary>
/// The ASP.NET [WebMethod] attribute sticks the payload in a "d" property for some reason, so objects are deserialized
/// into this fake container to work around this.
/// </summary>
public class JsonContainer
{
public object d { get; set; }
}
/// <summary>
/// Executes the JSON request, calling the completeAction delegate when done.
/// </summary>
/// <param name="url">The full URL to the ASP.NET web service.</param>
/// <param name="parameters">A name/value pair for the parameters to send to the URL. Use null if no parameters are required.</param>
/// <param name="completeAction">The delegate to call once the request completes. Use null if no action is required. The parameter
/// passed into this method can be the default value of T.</param>
public void Execute(string url, Dictionary<string, string> parameters, Action<T> completeAction)
{
// Check the url
if (string.IsNullOrEmpty(url))
throw new ArgumentNullException("The url parameter is null or empty");
// Check the func
if (completeAction == null)
throw new ArgumentNullException("The completeAction parameter is null or empty");
_completeAction = completeAction;
try
{
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
request.ContentType = "application/json";
request.Method = "POST";
// Write to the body with the parameters, if there are some
if (parameters != null && parameters.Count > 0)
{
State state = new State();
state.Request = request;
state.Parameters = parameters;
request.BeginGetRequestStream(LoadRequestBegin, state);
}
else
{
request.BeginGetResponse(LoadRequestComplete, request);
}
}
catch (WebException)
{
// If the exception is caught here, the complete action isn't called by the WebClient's async methods.
if (_completeAction != null)
_completeAction(default(T));
// For now, simply throw it back
throw;
}
}
/// <summary>
/// Writes parameters to the request body.
/// </summary>
/// <param name="result"></param>
private void LoadRequestBegin(IAsyncResult result)
{
State state = (State)result.AsyncState;
HttpWebRequest request = state.Request;
using (Stream stream = request.EndGetRequestStream(result))
{
using (StreamWriter writer = new StreamWriter(stream))
{
string jsonParams = BuildParameters(state.Parameters);
writer.Write(jsonParams);
}
}
request.BeginGetResponse(LoadRequestComplete, request);
}
/// <summary>
/// Manually builds the request parameters as a JSON string. This may already be in the JSON.NET library.
/// </summary>
/// <param name="parameters"></param>
/// <returns></returns>
private string BuildParameters(Dictionary<string, string> parameters)
{
List<string> list = new List<string>();
foreach (string key in parameters.Keys)
{
list.Add(string.Format("\"{0}\" : \"{1}\"", key, parameters[key]));
}
return "{" + string.Join(",", list.ToArray()) + "}";
}
/// <summary>
/// Gets the response of the request and fires the provided Action.
/// </summary>
/// <param name="result"></param>
private void LoadRequestComplete(IAsyncResult result)
{
T deserialized = default(T);
try
{
HttpWebRequest request = (HttpWebRequest)result.AsyncState;
// Get the response
WebResponse response = request.EndGetResponse(result);
using (Stream stream = response.GetResponseStream())
{
using (StreamReader reader = new StreamReader(stream))
{
string json = reader.ReadToEnd();
deserialized = Deserialize(json);
}
}
}
catch (WebException)
{
// This is swallowing the exception - you will want to do something more meaningful with it
}
finally
{
if (_completeAction != null)
_completeAction(deserialized);
}
}
/// <summary>
/// Deserializes the provided JSON to type T of the class.
/// </summary>
/// <param name="json"></param>
/// <returns></returns>
private T Deserialize(string json)
{
try
{
JsonSerializer serializer = new JsonSerializer();
// I'm not sure if ASP MVC uses the 'd' payload, so this a check might be needed here for it.
JsonContainer container = (JsonContainer)serializer.Deserialize(new StringReader(json), typeof(JsonContainer));
json = container.d.ToString();
return (T)serializer.Deserialize(new StringReader(json), typeof(T));
}
catch (InvalidCastException)
{
// You might prefer this to throw instead of returning a null
return default(T);
}
}
}
view raw gistfile1.cs hosted with ❤ by GitHub

csharpsilverlightwebclient

Chris Small

I'm Chris Small, a software engineer living and working in London. This is my tech blog. Find out more via GithubStackoverflowResume