2014-10-09 14 views
6

Mit dem MVC-Modell möchte ich ein JsonResult schreiben, die die JSON-Zeichenfolge an den Client streamen würde, anstatt alle Daten in JSON-String auf einmal konvertieren und dann streamen es zurück zum Kunden. Ich habe Aktionen, die sehr große (über 300.000 Datensätze) als Json-Übertragungen senden müssen und ich denke, dass die grundlegende JsonResult-Implementierung nicht skalierbar ist.Streaming große Liste von Daten als JSON-Format mit Json.net

Ich benutze Json.net, ich frage mich, ob es eine Möglichkeit gibt, die Stücke der JSON-Zeichenfolge zu streamen, wie es transformiert wird.

//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); 

Allerdings bin ich nicht sicher, wie ich die Stücke in Textwriter geschrieben bekommen und schreiben in Antwort und rufen reponse.Flush(), bis alle 300.000 Datensätze zu Json umgewandelt werden.

Ist das überhaupt möglich?

Antwort

12

Angenommen, Ihre endgültige Ausgabe ist ein JSON-Array und jeder "Chunk" ist ein Element in diesem Array, Sie könnten etwas wie die folgende Klasse versuchen. Es verwendet eine JsonTextWriter, um die JSON in den Ausgabestream zu schreiben, und verwendet eine JObject als Mittel, jedes Element einzeln zu serialisieren, bevor es in den Writer geschrieben wird. Sie können die Implementierung IEnumerable übergeben, die Elemente einzeln aus Ihrer Datenquelle lesen kann, damit Sie nicht alle im Speicher auf einmal haben. Ich habe das nicht ausgiebig getestet, aber es sollte dich in die richtige Richtung bringen.

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(); 
     } 
    } 
} 
+0

scheint sehr vielversprechend ... Ich werde es ausprobieren und u wissen, wie es geht ... – sam360

+0

Die Lösung gearbeitet, die aus der Erinnerung Ausnahme verhindert und das ist wunderbar. Aber ich denke, es wäre besser optimiert, wenn die Datensätze nacheinander zusammengespült würden. Nicht sicher, was die optimale Anzahl ist! – sam360

+0

Ja, darüber habe ich mich auch gewundert. Sie könnten dem JsonStreamingResult einfach einen Leistungsindikator hinzufügen, der auf Flush wartet, bis einige Datensätze aus dem Enumerable gelesen wurden. Wenn die Nummer je nach Situation unterschiedlich ist, können Sie sie als Parameter festlegen, damit Sie sie für jede andere Verwendung einstellen können. Auf der IEnumerable-Seite können Sie außerdem einen Mechanismus zum Abfragen Ihrer Datenquelle in Stapeln implementieren, um die Effizienz dort zu verbessern. Sie müssten viele Messungen und Tests durchführen, um zu sehen, was am besten funktioniert. –

Verwandte Themen