Deserializzare l’array json in streaming un elemento alla volta

Serializzo una serie di oggetti di grandi dimensioni su un stream di risposta http Json. Ora voglio deserializzare questi oggetti dal stream uno alla volta. Ci sono delle librerie c # che mi permetteranno di fare questo? Ho guardato json.net ma sembra che avrei dovuto deserializzare l’intera gamma di oggetti contemporaneamente.

[{large json object},{large json object}.....] 

Chiarimento: voglio leggere un object json dal stream alla volta e deserializzare.

Per leggere il JSON in modo incrementale, è necessario utilizzare un JsonTextReader in combinazione con uno StreamReader . Ma non devi necessariamente leggere tutto il JSON manualmente dal lettore. Dovresti essere in grado di sfruttare l’API Linq-To-JSON per caricare ogni object di grandi dimensioni dal lettore in modo che tu possa lavorare con esso più facilmente.

Per un semplice esempio, supponiamo di avere un file JSON simile a questo:

 [ { "name": "foo", "id": 1 }, { "name": "bar", "id": 2 }, { "name": "baz", "id": 3 } ] 

Il codice per leggerlo in modo incrementale dal file potrebbe essere simile al seguente. (Nel tuo caso sostituiresti FileStream con il tuo stream di risposta).

 using (FileStream fs = new FileStream(@"C:\temp\data.json", FileMode.Open, FileAccess.Read)) using (StreamReader sr = new StreamReader(fs)) using (JsonTextReader reader = new JsonTextReader(sr)) { while (reader.Read()) { if (reader.TokenType == JsonToken.StartObject) { // Load each object from the stream and do something with it JObject obj = JObject.Load(reader); Console.WriteLine(obj["id"] + " - " + obj["name"]); } } } 

L’output di quanto sopra sarebbe simile a questo:

 1 - foo 2 - bar 3 - baz 

Ho semplificato uno dei campioni / test del mio parser / deserializzatore per rispondere in modo più diretto a questo caso d’uso di questa domanda.

Ecco i dati del test:

https://github.com/ysharplanguage/FastJsonParser/tree/master/JsonTest/TestData

(cfr. fathers.json.txt)

Ed ecco il codice di esempio:

  using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; // Our stuff using System.Text.Json; //... public class FathersData { public Father[] fathers { get; set; } } public class Someone { public string name { get; set; } } public class Father : Someone { public int id { get; set; } public bool married { get; set; } // Lists... public List sons { get; set; } // ... or arrays for collections, that's fine: public Daughter[] daughters { get; set; } } public class Child : Someone { public int age { get; set; } } public class Son : Child { } public class Daughter : Child { public string maidenName { get; set; } } //... static void FilteredFatherStreamTestSimplified() { // Get our parser: var parser = new JsonParser(); // (Note this will be invoked thanks to the "filters" dictionary below) Func filteredFatherStreamCallback = obj => { Father father = (obj as Father); // Output only the individual fathers that the filters decided to keep (ie, when obj.Type equals typeof(Father)), // but don't output (even once) the resulting array (ie, when obj.Type equals typeof(Father[])): if (father != null) { Console.WriteLine("\t\tId : {0}\t\tName : {1}", father.id, father.name); } // Do not project the filtered data in any specific way otherwise, // just return it deserialized as-is: return obj; }; // Prepare our filter, and thus: // 1) we want only the last five (5) fathers (array index in the resulting "Father[]" >= 29,995), // (assuming we somehow have prior knowledge that the total count is 30,000) // and for each of them, // 2) we're interested in deserializing them with only their "id" and "name" properties var filters = new Dictionary>> { // We don't care about anything but these 2 properties: { typeof(Father), // Note the type (type, obj, key, index) => ((key as string) == "id" || (key as string) == "name") ? filteredFatherStreamCallback : JsonParser.Skip }, // We want to pick only the last 5 fathers from the source: { typeof(Father[]), // Note the type (type, obj, key, index) => (index >= 29995) ? filteredFatherStreamCallback : JsonParser.Skip } }; // Read, parse, and deserialize fathers.json.txt in a streamed fashion, // and using the above filters, along with the callback we've set up: using (var reader = new System.IO.StreamReader(FATHERS_TEST_FILE_PATH)) { FathersData data = parser.Parse(reader, filters); System.Diagnostics.Debug.Assert ( (data != null) && (data.fathers != null) && (data.fathers.Length == 5) ); foreach (var i in Enumerable.Range(29995, 5)) System.Diagnostics.Debug.Assert ( (data.fathers[i - 29995].id == i) && !String.IsNullOrEmpty(data.fathers[i - 29995].name) ); } Console.ReadKey(); } 

Il resto dei bit è disponibile qui:

https://github.com/ysharplanguage/FastJsonParser

‘HTH,

Questa è la mia soluzione (combinata da diverse fonti, ma principalmente basata sulla soluzione Brian Rogers ) per convertire un enorme file JSON (che è una matrice di oggetti) in un file XML per qualsiasi object generico.

JSON assomiglia a questo:

  { "Order": [ { order object 1}, { order object 2}, {...} { order object 10000}, ] } 

Output XML:

 ... ... ... 

Codice C #:

 XmlWriterSettings xws = new XmlWriterSettings { OmitXmlDeclaration = true }; using (StreamWriter sw = new StreamWriter(xmlFile)) using (FileStream fs = new FileStream(jsonFile, FileMode.Open, FileAccess.Read)) using (StreamReader sr = new StreamReader(fs)) using (JsonTextReader reader = new JsonTextReader(sr)) { //sw.Write(""); while (reader.Read()) { if (reader.TokenType == JsonToken.StartArray) { while (reader.Read()) { if (reader.TokenType == JsonToken.StartObject) { JObject obj = JObject.Load(reader); XmlDocument doc = JsonConvert.DeserializeXmlNode(obj.ToString(), "Order"); sw.Write(doc.InnerXml); // a line of XML code ... sw.Write("\n"); //this approach produces not strictly valid XML document //add root element at the beginning and at the end to make it valid XML } } } } //sw.Write(""); }