c# json serialization stream json.net

c# - Deserializar json array stream un elemento a la vez



serialization json.net (3)

Serializo una matriz de objetos grandes a una secuencia de respuesta http json. Ahora quiero deserializar estos objetos de la secuencia de a uno por vez. ¿Hay alguna biblioteca c # que me permita hacer esto? Miré json.net pero parece que tendría que deserializar la matriz completa de objetos a la vez.

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

Aclaración: quiero leer un objeto json de la secuencia a la vez y deserializarlo.


Esta es mi solución (combinada de diferentes fuentes, pero principalmente basada en la solución de ) para convertir un gran archivo JSON (que es una matriz de objetos) a un archivo XML para cualquier objeto genérico.

JSON se ve así:

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

Salida XML:

<Order>...</Order> <Order>...</Order> <Order>...</Order>

C # code:

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("<root>"); 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 <Order>...</Order> 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("</root>"); }


He simplificado una de las muestras / pruebas de mi analizador / deserializador para responder el caso de uso de esta pregunta de manera más directa.

Aquí está para los datos de prueba:

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

(cf. fathers.json.txt)

Y aquí está para el código de muestra:

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<Son> 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<object, object> filteredFatherStreamCallback = obj => { Father father = (obj as Father); // Output only the individual fathers that the filters decided to keep (i.e., when obj.Type equals typeof(Father)), // but don''t output (even once) the resulting array (i.e., 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<Type, Func<Type, object, object, int, Func<object, object>>> { // 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<FathersData>(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(); }

El resto de los bits está disponible aquí:

https://github.com/ysharplanguage/FastJsonParser

''HTH,


Para leer el JSON de forma incremental, deberá usar un JsonTextReader en combinación con un StreamReader . Pero, no necesariamente tiene que leer todo el JSON manualmente desde el lector. Debería poder aprovechar la API de Linq-To-JSON para cargar cada objeto grande del lector, de modo que pueda trabajar con él más fácilmente.

Para un ejemplo simple, digamos que tenía un archivo JSON que se veía así:

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

El código para leerlo de forma incremental desde el archivo puede ser similar al siguiente. (En su caso, reemplazará el FileStream con su flujo de respuesta).

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

La salida de lo anterior se vería así:

1 - foo 2 - bar 3 - baz