Подтвердить что ты не робот

Возвращение неуправляемого 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.

4b9b3361

Ответ 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>>();