Proper JSON Serialization in MVC 4

Proper JSON serialization in MVC 4

I would recommend using something like ServiceStack or Json.NET for handling Json output in your MVC application. However, you can easily write a class and override the Json method using a base class. See my example below.

NOTE: With this, you do not need anything in your Global.ascx.cs file.

Custom JsonDotNetResult class:

public class JsonDotNetResult : JsonResult
{
private static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
Converters = new List<JsonConverter> { new StringEnumConverter() }
};

public override void ExecuteResult(ControllerContext context)
{
if (this.JsonRequestBehavior == JsonRequestBehavior.DenyGet &&
string.Equals(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException("GET request not allowed");
}

var response = context.HttpContext.Response;

response.ContentType = !string.IsNullOrEmpty(this.ContentType) ? this.ContentType : "application/json";

if (this.ContentEncoding != null)
{
response.ContentEncoding = this.ContentEncoding;
}

if (this.Data == null)
{
return;
}

response.Write(JsonConvert.SerializeObject(this.Data, Settings));
}
}

Base Controller class:

public abstract class Controller : System.Web.Mvc.Controller
{
protected override JsonResult Json(object data, string contentType, System.Text.Encoding contentEncoding, JsonRequestBehavior behavior)
{
return new JsonDotNetResult
{
Data = data,
ContentType = contentType,
ContentEncoding = contentEncoding,
JsonRequestBehavior = behavior
};
}
}

Now, on your controller action you can simply return something like so.

return Json(myObject, JsonRequestBehavior.AllowGet);

BAM. You now have camelcase Objects returned with Json :)

NOTE: There are ways to do this with Serializer settings on each object that you make with Json. But who would want to type that out every time you want to return Json?

MVC 4 ApiController not serializing System.Json.JsonObject properly

So the answer is apparently... You Can't!

It appears that the type JsonPrimitive is not supported to serialize objects. The answers provided below by Obi and Dan helped me to poke around a bit more and find out that the XML serializer actually throws an exception while the JSON serializer simply eats it and puts out an empty array which is what you see above.

There are any number of correct answers here.

  1. Make your own custom serializer
  2. Output JSON as a string
  3. Return custom objects and then work around things like the Enum
    values

I am sure there are others.

But whatever you do don't try to use System.Json as a return in the ApiController because you will get the results above.

Customizing JSON serialization in MVC action output

Here's what I ended up with....

// Wrap "String" in a container class
public class JsonStringWrapper
{
// Hey Microsoft - This is where it would be nice if "String" wasn't marked "sealed"
public string theString { get; set; }
public JsonStringWrapper() { }
public JsonStringWrapper(string stringToWrap) { theString = stringToWrap; }
}

// Custom JsonConverter that will just dump the raw string into
// the serialization process. Loosely based on:
// http://www.newtonsoft.com/json/help/html/CustomJsonConverter.htm
public class JsonStringWrapperConverter : JsonConverter
{
private readonly Type _type = typeof(JsonStringWrapper);

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
JToken t = JToken.FromObject(value);

if (t.Type != JTokenType.Object)
{
t.WriteTo(writer);
}
else
{
string rawValue = ((JsonStringWrapper)value).theString;
writer.WriteRawValue((rawValue == null) ? "null" : rawValue);
}
}

public override bool CanWrite
{
get { return true; }
}

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException("Unnecessary because CanRead is false. The type will skip the converter.");
}

public override bool CanRead
{
get { return false; }
}

public override bool CanConvert(Type objectType)
{
return _type == objectType;
}
}

// Custom JsonResult that will use the converter above, largely based on:
// http://stackoverflow.com/questions/17244774/proper-json-serialization-in-mvc-4
public class PreSerializedJsonResult : JsonResult
{
private static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
{
Converters = new List<JsonConverter> { new JsonStringWrapperConverter() }
};

public override void ExecuteResult(ControllerContext context)
{
if (this.JsonRequestBehavior == JsonRequestBehavior.DenyGet &&
string.Equals(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException("GET request not allowed");
}

var response = context.HttpContext.Response;

response.ContentType = !string.IsNullOrEmpty(this.ContentType) ? this.ContentType : "application/json";

if (this.ContentEncoding != null)
{
response.ContentEncoding = this.ContentEncoding;
}

if (this.Data == null)
{
return;
}

response.Write(JsonConvert.SerializeObject(this.Data, Settings));
}
}

// My base controller method that overrides Json()...
protected JsonResult Json(string message, object data)
{
PreSerializedJsonResult output = new PreSerializedJsonResult();

object finalData = (data is string && (new char[] { '[', '{' }.Contains(((string)data).First())))
? new JsonStringWrapper(data as string)
: data;

output.Data = new
{
success = string.IsNullOrEmpty(message),
message = message,
data = finalData
};
output.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
output.MaxJsonLength = int.MaxValue;
return output;
}

// Aaaand finally, here's how it might get called from an Action method:
...
return Json("This was a failure", null);
...
return Json(null, yourJsonStringVariableHere);

With this, I'm not doing any Json parsing on the server. My string comes out of the database and goes straight to the client without MVC touching it.

EDIT: Updated version now also supports serializing objects that have individual properties somewhere in their hierarchy that are of type JsonStringWrapper. This is useful in my scenario to support a "hybrid" model. If object A has a property B that is one of my pre-baked JSON strings, the code above will properly handle that.

JSON serialization in asp.net MVC

You can create a generic model class and decorate its properties with JsonPropertyAttribute.

For example:

public class Response<T>
{
[JsonProperty("success")]
public bool Success { get; set; }

[JsonProperty("errorMessage")]
public string ErrorMessage { get; set; }

[JsonProperty("data")]
public T Data { get; set; }
}

Then you can use it instead of the anonymous object.

Setting the default JSON serializer in ASP.NET MVC

In ASP.Net MVC4 the default JavaScript serializer which is used in the JsonResult class is still the JavaScriptSerializer (you can check it in the code)

I think you have confused it with the ASP.Net Web.API where JSON.Net is the default JS serializer but MVC4 doesn't use it.

So you need to configure JSON.Net to work with MVC4 (basically you need to create your own JsonNetResult), there are plenty of articles about it:

  • ASP.NET MVC and Json.NET
  • Using JSON.NET as the default JSON serializer in ASP.NET MVC 3 - is it possible?

If you also want to use JSON.Net for controller action parameters so during the model binding then you need write your own ValueProviderFactory implementation.

And you need to register your implementation with:

ValueProviderFactories.Factories
.Remove(ValueProviderFactories.Factories
.OfType<JsonValueProviderFactory>().Single());
ValueProviderFactories.Factories.Add(new MyJsonValueProviderFactory());

You can use the built in JsonValueProviderFactory as an example or this article: ASP.NET MVC 3 – Improved JsonValueProviderFactory using Json.Net

Using the Json.NET serializer in an MVC4 project

Rick Strahl has a blog on that here with a code that works.



Related Topics



Leave a reply



Submit