Análisis de archivo json grande en .NET

He usado el método “JsonConvert.Deserialize (json)” de Json.Net hasta ahora, que funcionó bastante bien y para ser honesto, no necesitaba nada más que esto.

Estoy trabajando en una aplicación de fondo (consola) que descarga constantemente el contenido json desde diferentes URL, luego deserializa el resultado en una lista de objetos .Net.

using (WebClient client = new WebClient()) { string json = client.DownloadString(stringUrl); var result = JsonConvert.DeserializeObject<List>(json); } 

El simple fragmento de código anterior probablemente no parezca perfecto, pero cumple su función. Cuando el archivo es grande (15000 contactos – 48 mb de archivo), JsonConvert.DeserializeObject no es la solución y la línea arroja un tipo de excepción de JsonReaderException.

Json descargado es una matriz y así es como se ve una muestra. El contacto es una clase de contenedor para el objeto json deserializado.

 [ { "firstname": "sometext", "lastname": "sometext" }, { "firstname": "sometext", "lastname": "sometext" }, { "firstname": "sometext", "lastname": "sometext" }, { "firstname": "sometext", "lastname": "sometext" } ] 

Mi conjetura inicial es que se queda sin memoria. Solo por curiosidad, intenté analizarlo como JArray, que también causó la misma excepción.

Empecé a bucear en la documentación de Json.Net y leo hilos similares. Como todavía no he logrado producir una solución de trabajo, decidí publicar una pregunta aquí.

Agradecería cualquier consejo / fragmento de código que pudiera ayudarme a investigar el problema, aprender más sobre él y, finalmente, llegar a una solución.

Gracias 🙂

ACTUALIZACIÓN: Deserializando línea por línea, obtuve el mismo error: “[. Ruta ”, línea 600003, posición 1.” Entonces, lo que hice fue descargar dos de ellos y revisarlos en Notepad ++. Lo que noté es si la longitud de la matriz es más de 12000, después del 12000º elemento, “[” está cerrado y se inicia otra matriz. En otras palabras, el json se ve exactamente así:

 [ { "firstname": "sometext", "lastname": "sometext" }, { "firstname": "sometext", "lastname": "sometext" }, { "firstname": "sometext", "lastname": "sometext" }, { "firstname": "sometext", "lastname": "sometext" } ] [ { "firstname": "sometext", "lastname": "sometext" }, { "firstname": "sometext", "lastname": "sometext" }, { "firstname": "sometext", "lastname": "sometext" }, { "firstname": "sometext", "lastname": "sometext" } ] 

Como ha diagnosticado correctamente en su actualización, el problema es que el JSON tiene un cierre ] seguido de inmediato por una apertura [ para iniciar el siguiente conjunto. Este formato hace que el JSON no sea válido cuando se toma como un todo, y es por eso que Json.Net arroja un error. Afortunadamente, este problema parece surgir con la suficiente frecuencia que Json.Net en realidad tiene una configuración especial para manejarlo. Si utiliza un JsonTextReader directamente para leer el JSON, puede establecer el indicador SupportMultipleContent en true y luego usar un bucle para deserializar cada elemento individualmente. Esto debería permitirle procesar el JSON no estándar con éxito y de una manera eficiente con la memoria, independientemente de cuántas matrices hay o cuántos elementos hay en cada conjunto.

  using (WebClient client = new WebClient()) using (Stream stream = client.OpenRead(stringUrl)) using (StreamReader streamReader = new StreamReader(stream)) using (JsonTextReader reader = new JsonTextReader(streamReader)) { reader.SupportMultipleContent = true; var serializer = new JsonSerializer(); while (reader.Read()) { if (reader.TokenType == JsonToken.StartObject) { Contact c = serializer.Deserialize(reader); Console.WriteLine(c.FirstName + " " + c.LastName); } } } 

Demostración completa aquí: https://dotnetfiddle.net/2TQa8p

Json.NET admite la deserialización directamente de una secuencia. Aquí hay una forma de deserializar su JSON utilizando un StreamReader leyendo la cadena JSON de una en una en lugar de tener toda la cadena JSON cargada en la memoria.

 using (WebClient client = new WebClient()) { using (StreamReader sr = new StreamReader(client.OpenRead(stringUrl))) { using (JsonReader reader = new JsonTextReader(sr)) { JsonSerializer serializer = new JsonSerializer(); // read the json from a stream // json size doesn't matter because only a small piece is read at a time from the HTTP request IList result = serializer.Deserialize>(reader); } } } 

Referencia: consejos de rendimiento de JSON.NET

He hecho algo similar en python para el tamaño de archivo de 5 GB. Descargué el archivo en una ubicación temporal y lo leí línea por línea para formar un objeto JSON similar a cómo funciona SAX. Para c # utilizando json.net, puede descargar un archivo, usar el lector de flujo para leer el archivo y pasar esa secuencia a JsonTextReader y analizarlo en JObject usando JTokens.ReadFrom (su objeto JSonTextReader)