C# 使用JSON.net以JSON格式传输大量数据

C# 使用JSON.net以JSON格式传输大量数据,c#,json,asp.net-mvc,json.net,C#,Json,Asp.net Mvc,Json.net,使用MVC模型,我想编写一个JsonResult,将Json字符串流式传输到客户端,而不是将所有数据一次性转换为Json字符串,然后再流式传输回客户端。 我有一些操作需要以Json传输的形式发送非常大(超过300000条记录),我认为基本的JsonResult实现是不可伸缩的 我正在使用Json.net,我想知道是否有一种方法可以在转换Json字符串时对其进行流式处理 //Current implementation: response.Write(Newtonsoft.Json.JsonCon

使用MVC模型,我想编写一个JsonResult,将Json字符串流式传输到客户端,而不是将所有数据一次性转换为Json字符串,然后再流式传输回客户端。 我有一些操作需要以Json传输的形式发送非常大(超过300000条记录),我认为基本的JsonResult实现是不可伸缩的

我正在使用Json.net,我想知道是否有一种方法可以在转换Json字符串时对其进行流式处理

//Current implementation:
response.Write(Newtonsoft.Json.JsonConvert.SerializeObject(Data, formatting));
response.End();

//I know I can use the JsonSerializer instead
Newtonsoft.Json.JsonSerializer serializer = new Newtonsoft.Json.JsonSerializer();
serializer.Serialize(textWriter, Data);
但是,我不确定如何将这些块写入textWriter,写入response并调用reponse.Flush(),直到所有300000条记录都转换为Json


这可能吗?

假设您的最终输出是一个JSON数组,并且每个“块”都是该数组中的一项,您可以尝试以下类似的
JsonStreamingResult
类。它使用
JsonTextWriter
将JSON写入输出流,并使用
JObject
作为一种方法,在将每个项写入到编写器之前分别序列化。您可以传递
JsonStreamingResult
一个
IEnumerable
实现,该实现可以从您的数据源中单独读取项,这样您就不会一次将它们全部存储在内存中。我还没有对此进行过广泛的测试,但它应该会让你朝着正确的方向前进

public class JsonStreamingResult : ActionResult
{
    private IEnumerable itemsToSerialize;

    public JsonStreamingResult(IEnumerable itemsToSerialize)
    {
        this.itemsToSerialize = itemsToSerialize;
    }

    public override void ExecuteResult(ControllerContext context)
    {
        var response = context.HttpContext.Response;
        response.ContentType = "application/json";
        response.ContentEncoding = Encoding.UTF8;

        JsonSerializer serializer = new JsonSerializer();

        using (StreamWriter sw = new StreamWriter(response.OutputStream))
        using (JsonTextWriter writer = new JsonTextWriter(sw))
        {
            writer.WriteStartArray();
            foreach (object item in itemsToSerialize)
            {
                JObject obj = JObject.FromObject(item, serializer);
                obj.WriteTo(writer);
                writer.Flush();
            }
            writer.WriteEndArray();
        }
    }
}

将其留给.NET并等待缓冲区已满的问题还有其他问题

例如: 如果这样做,json的某些内容将被切断,从而导致前端出现解析问题

到目前为止,最好的方法是在每次迭代中使用批时刷新批处理,或者在每个项目中刷新批处理(如果这是您的设计目的的话)


目前,我使用SSE将数据推送到浏览器,并使用分隔符消息“on message end”向浏览器指示连接可以关闭,我知道SSE用例适用于连续流,但我们也可以使用它来帮助进行分块和批处理响应。

解决方案有效地防止了内存不足异常,这太棒了。但我认为,如果将成批记录一起刷新,而不是逐个刷新,则会更加优化。不确定最佳数字是多少!是的,我也很好奇。您可以很容易地向JsonStreamingResult添加一个计数器,这会导致它等待刷新,直到从枚举中读取了一定数量的记录。如果数字因情况不同而不同,则可以将其设置为参数,以便针对不同的用途对其进行调整。此外,在IEnumerable方面,您还可以实现一种机制来批量查询数据源,以提高效率。你需要做大量的测量和测试,看看什么是最好的。另一个想法是测量缓冲区大小,并以64KB或类似的速度刷新。不确定是否可以检查jsontextwriter中的数据大小如果您希望执行类似的操作,可以尝试使用。然而,这似乎表明.NET中的大多数流已经很好地优化了缓冲。如果是这样的话,也许最好根本不要调用
Flush
,而是让流在其内部缓冲区满时执行它的操作。但不确定;一些基准测试表明,最有效的方法是使用serializer.Serialize(writer,data);并一次性将所有数据传递给它,因为上面的注释指出流本身在处理缓冲区方面做得相当好,并且您的代码不需要执行一个巨大的循环:)