Incremental JSON Parsing en C #

Estoy tratando de analizar el JSON de forma incremental, es decir, en función de una condición.

Debajo está mi mensaje json y actualmente estoy usando JavaScriptSerializer para deserializar el mensaje.

string json = @"{"id":2, "method":"add", "params": {"object": {"name":"test" "id":"1"}, "position":"1"} }"; JavaScriptSerializer js = new JavaScriptSerializer(); Message m = js.Deserialize(json); 

La clase de mensaje se muestra a continuación:

  public class Message { public string id { get; set; } public string method { get; set; } public Params @params { get; set; } public string position { get; set; } } public class Params { public string name { get; set; } public string id{ get; set; } 

El código anterior analiza el mensaje sin problemas. Pero analiza todo el JSON de una vez. Quiero que siga procesando solo si el valor del parámetro “método” es “agregar”. Si no es “agregar”, entonces no quiero que proceda a analizar el rest del mensaje. ¿Hay alguna manera de hacer un análisis incremental basado en una condición en C #? (Medio ambiente: VS 2008 con .Net 3.5)

Tengo que admitir que no estoy tan familiarizado con el JavaScriptSerializer, pero si está abierto para usar JSON.net , tiene un JsonReader que actúa de manera muy parecida a un DataReader .

 using(var jsonReader = new JsonTextReader(myTextReader)){ while(jsonReader.Read()){ //evaluate the current node and whether it's the name you want if(jsonReader.TokenType.PropertyName=="add"){ //do what you want } else { //break out of loop. } } } 

Estos son los métodos generics y simples que uso para analizar, cargar y crear archivos JSON muy grandes. El código usa ahora una biblioteca JSON.Net bastante estándar. Desafortunadamente, la documentación no es muy clara sobre cómo hacer esto, pero tampoco es muy difícil de descifrar.

A continuación, el código asume el escenario en el que tiene una gran cantidad de objetos que desea serializar como matriz JSON y viceversa. Queremos admitir archivos muy grandes, el tamaño de whoes solo está limitado por su dispositivo de almacenamiento (no por la memoria). Por lo tanto, al serializar, el método toma IEnumerable y al deserializar devuelve lo mismo. De esta forma puede procesar todo el archivo sin estar limitado por la memoria.

He usado este código en tamaños de archivo de varios GB con un rendimiento razonable.

 //Serialize sequence of objects as JSON array in to a specified file public static void SerializeSequenceToJson(this IEnumerable sequence, string fileName) { using (var fileStream = File.CreateText(fileName)) SerializeSequenceToJson(sequence, fileStream); } //Deserialize specified file in to IEnumerable assuming it has array of JSON objects public static IEnumerable DeserializeSequenceFromJson(string fileName) { using (var fileStream = File.OpenText(fileName)) foreach (var responseJson in DeserializeSequenceFromJson(fileStream)) yield return responseJson; } //Utility methods to operate on streams instead of file public static void SerializeSequenceToJson(this IEnumerable sequence, TextWriter writeStream, Action progress = null) { using (var writer = new JsonTextWriter(writeStream)) { var serializer = new JsonSerializer(); writer.WriteStartArray(); long index = 0; foreach (var item in sequence) { if (progress != null) progress(item, index++); serializer.Serialize(writer, item); } writer.WriteEnd(); } } public static IEnumerable DeserializeSequenceFromJson(TextReader readerStream) { using (var reader = new JsonTextReader(readerStream)) { var serializer = new JsonSerializer(); if (!reader.Read() || reader.TokenType != JsonToken.StartArray) throw new Exception("Expected start of array in the deserialized json string"); while (reader.Read()) { if (reader.TokenType == JsonToken.EndArray) break; var item = serializer.Deserialize(reader); yield return item; } } } 

Si echa un vistazo a Json.NET , proporciona un analizador JSON que no es de caché y de solo reenvío, que se ajustará a sus necesidades.

Consulte la clase JsonReader y JsonTextReader en la documentación .

Actualmente estoy en la hora 3 de un intervalo de tiempo desconocido, viendo cómo 160GB de JSON se deserializan en objetos de clase. El uso de mi memoria se ha reducido a ~ 350MB, y cuando inspecciono objetos de memoria, es todo lo que el GC puede cuidar. Esto es lo que hice:

  FileStream fs = File.Open("F:\\Data\\mysuperbig150GB.json", FileMode.Open, FileAccess.Read, FileShare.ReadWrite); StreamReader sr = new StreamReader(fs); using (JsonReader reader = new JsonTextReader(sr)) { JsonSerializer serializer = new JsonSerializer(); MyJsonToClass result = serializer.Deserialize(reader); } 

El problema es la deserialización. Ese 160 GB de datos es mucho más grande que lo que mi PC puede manejar de una vez.

  1. Usé un pequeño fragmento (que es difícil, incluso acaba de abrir un archivo de 160 GB) y obtuve una estructura de clase a través de jsontochsarp .

  2. Hice una clase específica para la gran colección en la estructura de clases auto-generated-via-json-tool, y subestimé System.Collection.ObjectModel.ObservableCollection en lugar de List. Ambos implementan IEnumberable, que creo que es todo lo que le importa al deserializador JSON de Newtsonsoft.

  3. Entré y eliminé InsertItem, así:

     protected override void InsertItem(int index, Feature item) { //do something with the item that just got deserialized //stick it in a database, etc. RemoveItem(0); } 

Nuevamente, mis problemas fueron parcialmente sobre la velocidad de deserialización de JSON, pero más allá de eso no pude ajustar ~ 160GB de datos JSON en la colección. Incluso más estricto, sería en el área de docenas de conciertos, mucho más grande de lo que .net va a estar feliz.

InsertItem en ObservableCollection es el único método que conozco que puede manejar cuando ocurre la deserialización. List.Add () no. Sé que esta solución no es “elegante”, pero está funcionando mientras escribo esto.

Querrías un analizador de tipo SAX para JSON

http://en.wikipedia.org/wiki/Simple_API_for_XML

http://www.saxproject.org/event.html

SAX plantea un evento a medida que analiza cada parte del documento.

Hacer algo así en JSON sería (debería) ser bastante simple, dado lo simple que es la syntax JSON.

Esta pregunta podría ser de ayuda: ¿hay una API de transmisión para JSON?

Y otro enlace: https://www.p6r.com/articles/2008/05/22/a-sax-like-parser-for-json/

¿Cuál es el motivo de este enfoque? Si se trata de rendimiento, entonces es probable que se trate de una “optimización prematura” o, en otras palabras, de preocuparse por un problema que podría no existir.

Recomiendo encarecidamente que no se preocupe por este detalle. Cree su aplicación y, si no es lo suficientemente rápido, use herramientas de creación de perfiles para localizar los cuellos de botella reales : es probable que no estén donde usted espera.

Centrarse en el rendimiento antes de saber que es un problema casi siempre conduce a la pérdida de tiempo y al código excesivo.