Analisi JSON incrementale in C #

Sto cercando di analizzare il JSON in modo incrementale, cioè in base a una condizione.

Di seguito è riportato il mio messaggio JSON e attualmente sto usando JavaScriptSerializer per deserializzare il messaggio.

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

La class del messaggio è la seguente:

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

Il codice precedente analizza il messaggio senza problemi. Ma analizza l’intero JSON contemporaneamente. Voglio che proceda all’analisi solo se il valore del parametro “metodo” è “aggiungi”. Se non è “aggiungi”, allora non voglio che proceda ad analizzare il resto del messaggio. C’è un modo per fare l’analisi incrementale in base a una condizione in C #? (Ambiente: VS 2008 con .Net 3.5)

Devo ammettere che non ho familiarità con JavaScriptSerializer, ma se sei aperto a utilizzare JSON.net , ha un JsonReader che funziona come 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. } } } 

Ecco i metodi generici e semplici che utilizzo per analizzare, caricare e creare file JSON molto grandi. Il codice utilizza ora una libreria standard JSON.Net . Sfortunatamente la documentazione non è molto chiara su come farlo, ma non è nemmeno difficile da capire.

Sotto il codice si presuppone lo scenario in cui si dispone di un numero elevato di oggetti che si desidera serializzare come array JSON e viceversa. Vogliamo supportare file di dimensioni molto grandi, ma la dimensione è limitata solo dal dispositivo di archiviazione (non dalla memoria). Quindi, durante la serializzazione, il metodo accetta IEnumerable e mentre deserializza restituisce lo stesso. In questo modo è ansible elaborare l’intero file senza essere limitato dalla memoria.

Ho usato questo codice su file di diverse GB con prestazioni ragionevoli.

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

Se date un’occhiata a Json.NET , fornisce un parser JSON non-caching, forward-only che soddisferà le vostre esigenze.

Vedere la class JsonReader e JsonTextReader nella documentazione .

Sono attualmente nell’ora 3 di un intervallo di tempo sconosciuto, guardando 160 GB di JSON che vengono deserializzati in oggetti di class. Il mio uso della memoria è stato sospeso a ~ 350 MB, e quando ispeziono oggetti di memoria è tutto ciò che il GC può fare. Ecco cosa ho fatto:

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

Il problema è la deserializzazione. I 160 GB di dati sono molto più grandi di quelli che il mio PC può gestire contemporaneamente.

  1. Ho usato un piccolo snippet (che è difficile, anche solo aprendo un file da 160 GB) e ho ottenuto una struttura di class tramite jsontochsarp .

  2. Ho creato una class specifica per la grande raccolta nella struttura della class auto-generated-via-json-tool e ho sottoclassato System.Collection.ObjectModel.ObservableCollection anziché List. Entrambi implementano IEnumberable, che credo sia interessato a tutto il deserializzatore JSON di Newtsonsoft.

  3. Sono entrato e ho sovrascritto InsertItem, in questo modo:

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

Ancora una volta, i miei problemi in cui la deserializzazione JSON è in parte limitata, ma oltre a ciò non sono in grado di contenere circa 160 GB di dati JSON nella raccolta. Anche rafforzato, sarebbe nelle dozzine di aree di concerti, molto più grandi di quelle con cui .net sarà felice.

InsertItem su ObservableCollection è l’unico metodo di cui sono a conoscenza che è ansible gestire quando si verifica la deserializzazione. List.Add () no. So che questa soluzione non è “elegante”, ma funziona mentre scrivo questo.

Vorresti un parser di tipo SAX per JSON

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

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

SAX genera un evento analizzando ogni parte del documento.

Fare qualcosa del genere in JSON sarebbe (dovrebbe) essere piuttosto semplice, data la semplicità della syntax JSON.

Questa domanda potrebbe essere d’aiuto: esiste una API di streaming per JSON?

E un altro link: https://www.p6r.com/articles/2008/05/22/a-sax-like-parser-for-json/

Qual è la ragione di questo approccio? Se si tratta di prestazioni, è probabile che si tratti di “ottimizzazione prematura” o, in altre parole, di un problema che potrebbe non esistere.

Vorrei fortemente esortare che non ti preoccupi di questo dettaglio. Costruisci la tua applicazione e, se non è abbastanza veloce, usa gli strumenti di profilazione per individuare i colli di bottiglia effettivi : probabilmente non saranno quelli che ti aspetti.

Concentrarsi sulle prestazioni prima di sapere che si tratta di un problema porta quasi sempre alla perdita di tempo e al codice eccessivo.