20

How does one return unescaped Json, using Json.Net in an MVC project?

So far, I serialize a basic object, and get Json.Net to serialize it:

public JsonResult GetTimelineJson()
{
    var result = new MyGraph([some data...]);

    return Json(JsonConvert.SerializeObject(result), JsonRequestBehavior.AllowGet);
}

Result:

"{\r\n  \"id\": \"myGraph\",\r\n  \"title\": \"Graph title\",\r\n [...]

Any attempts to wrap it an an HtmlString, etc, result in an empty set being passed across the wire (though the debug point shows it correctly un-escaped). I've checked that the content-type is set correctly in the HTTP headers.

ΩmegaMan
  • 29,542
  • 12
  • 100
  • 122
Overflew
  • 7,872
  • 10
  • 45
  • 65
  • 2
    Why are you sending a JSON-serialized string as a JSON result? Either use the model directly with the JSON method (no need for JSON.Net) or return your serialized object using `Content()` with the correct MIME type. – tvanfosson Sep 12 '11 at 00:43
  • I came here looking for Json formatted results to be returned from a Web API method, and after some hunting on SO, I finally got the expected results. See my answer [here](http://stackoverflow.com/questions/12240713/put-content-in-httpresponsemessage-object/43188777#43188777). – Adam Cox Apr 03 '17 at 20:30

4 Answers4

34

You can also do this

public ActionResult GetTimelineJson()
{
    var result = new MyGraph([some data...]);
    return Content(JsonConvert.SerializeObject(result), "application/json");
}

Note that you should change return type from JsonResult to ActionResult

Cagatay Kalan
  • 4,066
  • 1
  • 30
  • 23
  • 6
    This is the best answer. And in general, if one would like to return unescaped and unformatted JObject to the client, then there is `ToString(Formatting)` in JObject class and it can be used just like this: `JObject o = JObject.Parse("{foo:1000, bar: 2000}"); return Content(o.ToString(Formatting.None), "application/json")`; – Mateusz Szulc Nov 19 '13 at 10:28
32

The object is already serialized by Json.NET, and when you pass it to Json() it gets encoded twice. If you must use Json.NET instead of the built in encoder, then the ideal way to handle this would be to create a custom ActionResult accepts the object and calls Json.net internally to serialize the object and return it as an application/json result.

EDIT

This code is for the solution mentioned above. It's untested, but should work.

public class JsonDotNetResult : ActionResult
{
    private object _obj { get; set; }
    public JsonDotNetResult(object obj)
    {
        _obj = obj;
    }

    public override void ExecuteResult(ControllerContext context)
    {
        context.HttpContext.Response.AddHeader("content-type", "application/json");
        context.HttpContext.Response.Write(JsonConvert.SerializeObject(_obj));
    }
}

and in your controller just do:

return new JsonDotNetResult(result);
Ryan Wright
  • 3,333
  • 2
  • 16
  • 9
5

You are Jsoning it twice, the Json method is json serializing your already converted string. If you want to use JsonConvert then write that directly to the response stream.

kmcc049
  • 2,783
  • 17
  • 13
1

I made a slight change to my new class to make unit testing easier:

public class JsonDotNetResult : ActionResult
{
    public JsonDotNetResult(object data)
    {
        Data = data;
    }

    //Name the property Data and make the getter public
    public object Data { get; private set; }

    public override void ExecuteResult(ControllerContext context)
    {
        context.HttpContext.Response.AddHeader("content-type", "application/json");
        context.HttpContext.Response.Write(JsonConvert.SerializeObject(Data));
    }
}

}

This more closely resembles JsonResult in System.Web.Mvc and allows me to unit test either with a generic method...

System.Web.Mvc.JsonResult

Unit test helper:

public static TReturn GetDataFromJsonResult<TJsonType, TReturn>(this ActionResult result) where TJsonType : ActionResult
{
    var jsonResult = (TJsonType)result;

    var data = jsonResult.GetType().GetProperty("Data").GetValue(jsonResult);

    return (TReturn)data;
}

Unit Test Example:

[TestMethod]
public void ControllerMethod_WhenMethodCalled_ThenSomeRecordsAreReturned()
{
    // arrange
    var records = new List<string> { "Record1", "Record2" };
    var expectedRecordCount = records.Count();

    myService.Setup(x => x.GetRecordsFromDatabase()).Returns(records);

    // act
    var result = myController.GetRecords(); //Assuming this controller method returns JsonDotNetResult

    // assert
    var jsonResult = result.GetDataFromJsonResult<JsonDotNetResult, IEnumerable<string>>();
    Assert.AreEqual(expectedRecordCount, jsonResult.Count());
}

This line can be changed if the controller return the normal JsonResult:

    var jsonResult = result.GetDataFromJsonResult<JsonResult, IEnumerable<string>>();