An alternative to JsonResult which removes nulls
This post goes through a few steps before the core problem becomes clear so please bear with me. Also I would be very interested in hearing what people think of my solution so please comment if you can. I was in the course of building a rest api using asp.net mvc which returns json from its end points.
As one might expect I started by using JsonResult as the return type from my controllers. This is built into MVC and inherits from Actionresult adding some validation and setting the content-type of the response.
My inital controller action looked like this
public JsonResult Single()
{
var user = new User() { name = "David Hume" };
return Json(user,JsonRequestBehavior.AllowGet);
}
This works fine and the controller correctly returns
{"name":"David Hume"} with the content-type set to applicaiton/json.
So far so good. However if we have a null user name we get ugliness.
public JsonResult SingleEmpty()
{
var user = new User();
return Json(user, JsonRequestBehavior.AllowGet);
}
Perhaps user is not a good example but there will be many types returned by my api with nullable properties. But then i dont want these null properties serialised. SingleEmpty however returns
{"name":null}
So i then started looking into how i can tell the serialiser to ignore null properties and the can of worms began to open.
The Json() serialiser is needed to return JsonResult objects, unfortunatly the serialiser JsonResult uses is based on the once deprecated then reinstated .Net javascript serialiser whose very limited options do not include ignoring null properties. The suggested method on SO seemed to be to write my own JavaScriptConverter and register it using the RegisterConverters method. This seemed like a potenitally flaky solution with considerable work just to get a relativly simple feature.
This led me back to tried and tested json.net library from James Newton-King. This has all the options i need so i rewrote the controller like this
public JsonResult Double()
{
var user = new User() { name = "David Hume" };
return Json(JsonConvert.SerializeObject(user, Formatting.None, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }), JsonRequestBehavior.AllowGet);
}
This sucessfully removes the null properties but as you might expect it serialises twice. Once with the json.net and once in the JsonResult. This returns "{\"name\":\"David Hume\"}"
However the second cast cannot be reomved whilst still retuning the JsonResult type.
After searching around the net i decied to abandon the JsonResult type altogether as the clients apps only see the object as a string anyway so all JsonResult is really doing is setting the content-type in the response header for me.
So i returned a string and set the content-type manually. I have also moved the formating and properites for the json into a helper method
public static class FormatHelpers
{
public static string FormattedResult(User user)
{
return JsonConvert.SerializeObject(user, Formatting.None, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
}
}
public string Index()
{
Response.ContentType = "application/json";
var user = new User() { name = "David Hume" };
return FormatHelpers.FormattedResult(user);
}
This now ignores null properties and returns correctly serialised application/json.
One final step to make this a little tidier and more robust is to avoid having to set the content-type in every controller action in the api. I can set this everywhere by creating a new controller which overirdes the OnActionExecuting method of the MVC base controller. I can then inherit from that controller in all my api controllers and thereby ensure the content-type is correctly set for all the actions. I can also use this design pattern to create other global features of my endpoint like having all returns wrapped in a result tag which shows a status.
So here is my custom base controller
public class JsonController : Controller
{
public Result result;
public JsonController()
{
result = new Result();
}
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
Response.ContentType = "application/json";
}
}
Notice how the result object is created here in the contructor when the controller is instanitated and the content-type is set. Then i simply inherit from the controller in my api end points and the result object is available and the content-type is set for me. i also edit my formatting helper to accept and return the global result object.
public static string FormattedResult(Result result)
{
return JsonConvert.SerializeObject(result, Formatting.None, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
}
public class MyController : JsonController
{
public string Index()
{
result.status = "ok";
result.User = new User() { name = "David Hume" };
return FormatHelpers.FormattedResult(result);
}
}
So after a fair amount of hassle i have my actions returning good json. There is a lot of interesting stuff you can do by overriding the methods in the base controller which i will write another post on soon. In case anything here isnt clear the working sln is on git hub
Comments