Analizar archivos json grandes in.NET


He usado "JsonConvert.Deserialize (json)" método 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 de 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<Contact>>(json);

 }

El simple fragmento de código anterior probablemente no parece perfecto, pero hace el trabajo. Cuando el archivo es grande (15000 contactos-archivo de 48 mb), JsonConvert.DeserializeObject no es la solución y la línea lanza un tipo de excepción de JsonReaderException.

El json descargado es una matriz y así es como se ve una muestra. Contact es una clase 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, traté de analizarlo como JArray que causó la misma excepción también.

He comenzado a sumergirme en Json.Net documentación y leer temas similares. Como yo aún 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: Mientras deserializaba línea por línea, obtuve el mismo error:" [. Path", línea 600003, posición 1."Así que lo que hice fue descargar dos de ellos y comprobarlos en Notepad++. Lo que noté es que si la longitud de la matriz es más de 12000, después del elemento 12000 el " ["se cierra y se inicia otro array. 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"
  }
]
Author: Yavarino, 2015-08-26

3 answers

Como has diagnosticado correctamente en tu actualización, el problema es que el JSON tiene un cierre ] seguido inmediatamente 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 lanza un error. Afortunadamente, este problema parece surgir con suficiente frecuencia que Json.Net en realidad tiene un entorno especial para lidiar con él. Si usa un JsonTextReader directamente para leer el JSON, puede establecer la bandera 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 manera eficiente en memoria, independientemente de cuántos arrays haya o cuántos elementos en cada array.

    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<Contact>(reader);
                Console.WriteLine(c.FirstName + " " + c.LastName);
            }
        }
    }

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

 32
Author: Brian Rogers,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-06 19:19:10

Json.NET admite la deserialización directamente desde una transmisión. Aquí hay una manera de deserializar su JSON usando un StreamReader leyendo la cadena JSON una pieza a la vez 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<Contact> result = serializer.Deserialize<List<Contact>>(reader);
        }
    }
}

Referencia: JSON.NET Consejos de Rendimiento

 14
Author: Kristian Vukusic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-26 20:57:06

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

 5
Author: nixdaemon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-26 13:21:19