Возвращение неуправляемого Json в MVC с помощью Json.Net
Как вернуть неэкранированный Json, используя Json.Net в проекте MVC?
Пока что я сериализую базовый объект и получаю Json.Net для его сериализации:
public JsonResult GetTimelineJson()
{
var result = new MyGraph([some data...]);
return Json(JsonConvert.SerializeObject(result), JsonRequestBehavior.AllowGet);
}
Результат:
"{\r\n \"id\": \"myGraph\",\r\n \"title\": \"Graph title\",\r\n [...]
Любые попытки обернуть его в HtmlString и т.д. Приводят к тому, что через набор передается пустой набор (хотя точка отладки показывает, что он правильно не экранирован). Я проверил, что тип содержимого установлен правильно в заголовках HTTP.
Ответы
Ответ 1
Объект уже сериализуется Json.NET, и когда вы передаете его Json(), он дважды кодируется. Если вы должны использовать Json.NET вместо встроенного кодировщика, то идеальным способом справиться с этим было бы создание пользовательского ActionResult, принимающего объект, и вызовы Json.net внутри, чтобы сериализовать объект и вернуть его как результат приложения /json.
ИЗМЕНИТЬ
Этот код предназначен для решения, упомянутого выше. Он не тестировался, но должен работать.
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));
}
}
и в вашем контроллере просто выполните:
return new JsonDotNetResult(result);
Ответ 2
Вы также можете сделать это
public ActionResult GetTimelineJson()
{
var result = new MyGraph([some data...]);
return Content(JsonConvert.SerializeObject(result), "application/json");
}
Обратите внимание, что вы должны изменить тип возврата из JsonResult в ActionResult
Ответ 3
Вы разбираете его дважды, метод Json
представляет собой серию json для вашей уже преобразованной строки. Если вы хотите использовать JsonConvert, тогда пишите это непосредственно в поток ответов.
Ответ 4
Я немного изменил свой новый класс, чтобы упростить модульное тестирование:
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));
}
}
}
Это более похоже на JsonResult в System.Web.Mvc и позволяет мне unit test либо с помощью общего метода...
![System.Web.Mvc.JsonResult]()
Unit test помощник:
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 Пример:
[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());
}
Эта строка может быть изменена, если контроллер вернет нормальный JsonResult:
var jsonResult = result.GetDataFromJsonResult<JsonResult, IEnumerable<string>>();