使用JSON.NET返回ActionResult

55

我正在尝试编写一个C#方法,用于序列化模型并返回JSON结果。这是我的代码:

    public ActionResult Read([DataSourceRequest] DataSourceRequest request)
    {
        var items = db.Words.Take(1).ToList();
        JsonSerializerSettings jsSettings = new JsonSerializerSettings();
        jsSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
        var converted = JsonConvert.SerializeObject(items, null, jsSettings);
        return Json(converted, JsonRequestBehavior.AllowGet);
    }

当我在Chrome中访问Words / Read时,我得到了以下JSON结果:

"[{\"WordId\":1,\"Rank\":1,\"PartOfSpeech\":\"article\",\"Image\":\"Upload/29/1/Capture1.PNG\",\"FrequencyNumber\":\"22038615\",\"Article\":null,\"ClarificationText\":null,\"WordName\":\"the | article\",\"MasterId\":0,\"SoundFileUrl\":\"/UploadSound/7fd752a6-97ef-4a99-b324-a160295b8ac4/1/sixty_vocab_click_button.mp3\",\"LangId\":1,\"CatId\":null,\"IsActive\":false}

我认为转义引号的问题出现在双重序列化对象时。来自其他问题: WCF JSON输出中添加了不必要的引号和反斜杠

看起来我确实对对象进行了双重序列化,因为我首先使用JSON.NET进行序列化,然后将结果传递给Json()函数。由于需要避免引用循环,我需要手动进行序列化,但我认为我的View需要一个ActionResult。

我该如何在这里返回一个ActionResult?我需要这样做吗,还是只需返回字符串即可?


在 JavaScript 的返回值中,我只需要执行 JSON.parse(message)。 - MiniRagnarok
你的意思是使用 Javascript 解析 /" 吗?我正在使用 Razor HTML helpers 进行特定包裹,所以我不知道该怎么做才能使其工作。 - hubatish
2个回答

89

我发现了一个类似的stackoverflow问题:Json.Net 和 ActionResult

那里的答案建议使用

return Content( converted, "application/json" );

那似乎在我的非常简单的页面上起作用。


4
IMO最佳答案。简单明了,恰好满足要求。 - pim
2
如果您检查标题,您会发现您可能具有“text/html”作为内容类型。 - Rafael Herscovici
迄今为止最简单的解决方案。它恰好做到了它需要做的事情。 - user1751825
基于hubatish的答案:<code> public abstract class ControllerBase : Controller { protected const string JsonEncoding = "application/json; charset=utf-8"; protected ContentResult JsonNetResult(object result) { string json = JsonConvert.SerializeObject(result); return Content(json, JsonEncoding); } }</code> - Roman M
@Dementic 或许在4年后有些变化,但使用这个答案中的语法,我的响应头显示“content-type: application/json”。 - Philip Stratford

69

为什么不在控制器中(或基本控制器中)覆盖Json()方法以增强其可重用性,而不是使用JSON.NET进行序列化,然后调用Json()呢?

这是从这篇博客文章中摘录的。

在您的控制器(或基本控制器)中:

protected override JsonResult Json(
        object data,
        string contentType,
        System.Text.Encoding contentEncoding,
        JsonRequestBehavior behavior)
{
    return new JsonNetResult
    {
        Data = data,
        ContentType = contentType,
        ContentEncoding = contentEncoding,
        JsonRequestBehavior = behavior
    };
}

并且JsonNetResult的定义如下:

public class JsonNetResult : JsonResult
{
    public JsonNetResult()
    {
        Settings = new JsonSerializerSettings
        {
            ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
        };
    }

    public JsonSerializerSettings Settings { get; private set; }

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


        HttpResponseBase response = context.HttpContext.Response;
        response.ContentType =
            string.IsNullOrEmpty(this.ContentType) ? "application/json" : this.ContentType;

        if (this.ContentEncoding != null)
            response.ContentEncoding = this.ContentEncoding;
        if (this.Data == null)
            return;

        var scriptSerializer = JsonSerializer.Create(this.Settings);

        using (var sw = new StringWriter())
        {
            scriptSerializer.Serialize(sw, this.Data);
            response.Write(sw.ToString());
        }
    }
}

通过这样做,当您在控制器中调用Json()时,您将自动获得所需的JSON.NET序列化。


很好。这非常方便。此外,我在想这将允许我仍然传递JSONRequestBehavior.AllowGet,但似乎并没有完全起作用。 - hubatish
2
只需将 contentTypecontentEncoding 设为 null,即可获得与默认实现相同的结果。否则,添加一个重载到 Json(),仅接受 databehavior,并为 contentTypecontentEncoding 提供 null 值,然后使用我在答案中提供的所有参数调用该实现。 - Sven Grosen
@hubatish本人的回答更好。 - Fayyaz Naqvi
2
JsonNetResult 的 AllowGet 执行似乎与应用在方法上的属性多余了 - 让我感到惊讶的是我已经有了 AcceptVerbsAttribute ,但仍需要多余地启用 AllowGet。需要找出如何从 ControllerContext 中读取 AcceptVerbsAttribute。 - Pxtl
1
非常好的回答。我的问题是关于最后一部分:是将所有内容序列化到StringWriter中,然后写入HTTP响应更好,还是直接写入输出流更有效?using (var sw = new StreamWriter(response.OutputStream, response.ContentEncoding)) { scriptSerializer.Serialize(sw, Data); sw.Flush(); } - Joshua
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接