2012-01-26 17 views
11

Estoy intentando analizar el JSON de forma incremental, es decir, en función de una condición.Análisis JSON incremental en C#

A continuación se muestra 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<Message>(json); 

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 resto 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)

+2

¿El análisis de Json es crítico para usted? – CodesInChaos

+0

¿Por qué exactamente quieres hacer esto? ¿El objeto analizado toma demasiada memoria? O es demasiado lento (¿lo has medido?)? ¿O tienes alguna otra razón? – svick

Respuesta

14

tengo que admitir que no soy tan familiarizados con el JavaScriptSerializer, pero si usted está abierto a utilizar JSON.net, que tiene una JsonReader que actúa como 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. 
    } 
    } 
} 
+0

¡Eso funcionó! ¡Estupendo! Gracias .. – user591410

+0

Me alegro de poder ayudar! –

+0

¿Comprueba la sintaxis? ¿O deja la estructura JSON de validación a discreción del desarrollador? – SerG

0

¿Cuál es la razón 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.

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

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

+3

Definitivamente un punto válido, y tengo la sensación de que tienes razón, pero no responde la pregunta del OP. –

+0

Gracias por sus entradas. Tiene razón, pero el mensaje json que se ilustró en mi pregunta no es el mensaje real, se ve así (se muestra a continuación). Por lo tanto, quiero analizar solo lo que se necesita según la condición. '{" id ": 2, " method ":" add ", " params ": {" object ": {" name ":" test ", " key2 ":" value2 ".... "key100": "value: 100"}, "position": "1"} } "; – user591410

+0

@ user591410, si analiza un mensaje innecesario por completo, ¿cuántos nano segundos pierde? –

1

serías que desean un analizador de tipo SAX para JSON

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

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

SAX provoca un evento, ya que analiza cada pieza del documento.

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

Esta pregunta podría ser de ayuda: Is there a streaming API for JSON?

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

+0

Gracias por los enlaces ... lo investigaremos. – user591410

5

Si se echa un vistazo a Json.NET, se proporciona un no-caching, sólo avance analizador JSON que se adapte a sus necesidades .

Consulte la clase JsonReader y JsonTextReader en el documentation.

+0

Entendido ... Gracias por los indicadores ... – user591410

+0

[Deserialización de fragmentos JSON parciales] (http: //www.newtonsoft.com/json/help/html/SerializingJSONFragments.htm) –

8

Éstos son los métodos genéricos y sencillas que utilizo para analizar, carga y crear archivos muy grandes JSON. El código usa ahora prácticamente la biblioteca estándar JSON.Net. Desafortunadamente, la documentación no es muy clara sobre cómo hacer esto, pero tampoco es muy difícil de descifrar.

El siguiente código supone 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<T> y al deserializar devuelve lo mismo. De esta forma puede procesar todo el archivo sin estar limitado por la memoria.

Tengo used this code 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<T>(this IEnumerable<T> 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<T> DeserializeSequenceFromJson<T>(string fileName) 
{ 
    using (var fileStream = File.OpenText(fileName)) 
     foreach (var responseJson in DeserializeSequenceFromJson<T>(fileStream)) 
      yield return responseJson; 
} 

//Utility methods to operate on streams instead of file 
public static void SerializeSequenceToJson<T>(this IEnumerable<T> sequence, TextWriter writeStream, Action<T, long> 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<T> DeserializeSequenceFromJson<T>(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<T>(reader); 
      yield return item; 
     } 
    } 
} 
2

Actualmente estoy en la hora 3 de un período de tiempo desconocido, viendo 160 GB de JSON obtener deserializado 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<MyJsonToClass>(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. que utiliza un pequeño fragmento (que es difícil, incluso con sólo abrir un archivo de 160 GB) y tiene una estructura de clases 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 subclasificó 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 InsertItem hizo caso omiso, como esto:

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

Una vez más, mis problemas donde parcialmente sobre JSON velocidad deserialización pero más allá de que no podía adaptarse a ~ 160 GB 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.