Etiqueta: JSON

  • Cómo trabajar con grandes archivos JSON usando PHP

    Cómo trabajar con grandes archivos JSON usando PHP

    Recorrer un archivo json es algo sencillo, ¿cierto? Pues parece que cuando el tamaño del archivo es grande las cosas ya no son tan simples…

    Lo que se haría típicamente sería algo similar a:

    <?php
    
    $json = json_decode( file_get_contents( $argv[1] ), true );
    
    print_r( $json );

    El problema aquí es que se requiere primero leer todo el archivo para, recién entonces empezar a procesar…

    Tomemos un ejemplo como el árbol de categorías de MercadoLibre Brasil. Este archivo pesa 229 MB (Una vez descomprimido).

    229 MB de puro json… es bastante, sí, pero… ¿es mucho?

    La respuesta en definitiva depende la configuración que tengas en tu php.ini.

    A efectos de este análisis hice un cambio al mío de modo que quede así:

    ; Maximum amount of memory a script may consume (128MB)
    ; http://php.net/memory-limit
    memory_limit = 128

    128 MB no es algo inusual para una instalación de PHP (Al día de hoy, es el valor por defecto), con lo cual, estaremos en un problema al tratar de leer un archivo de más de 200…

    Aumentar la memoria disponible

    Una solución posible es darle más holgura a nuestro intérprete (En este caso, dejarle ocupar más memoria).

    Podemos hacerlo cambiando la configuración a algo como:

    ; Maximum amount of memory a script may consume (128MB)
    ; http://php.net/memory-limit
    memory_limit = 256

    Pero eso es, como decimos en mi país, «Pan para hoy, hambre para mañana».

    Si elegimos esta ruta (y queremos asegurarnos de estar bien cubiertos), lo ideal sería hacer esto:

    ; Maximum amount of memory a script may consume (128MB)
    ; http://php.net/memory-limit
    memory_limit = -1

    Y ahí el limitante que tendremos será la memoria física de la computadora… algo drástico para mi gusto y, más aún, poco practicable.

    El problema es que no siempre tenemos acceso a la configuración de php (como por ejemplo, en un hosting compartido).

    Aún si lo tuviéramos, tengo dudas sobre la posibilidad de alterar un setting tan sensible como este, ya que al aprovecharnos de los recursos de una forma tan poco medida estaríamos muy probablemente afectando a nuestros vecinos.

    Mejorar el modo de procesar JSON

    Otra forma de resolver el problema es cambiar la librería que usamos para procesar JSON.

    Existen varias alternativas pero en este artículo quiero mostrarte JsonReader (Una extensión de PHP).

    Una vez instalada, es bastante fácil de usar. Veamos un ejemplo de cómo obtener todos los nombres de los artículos del archivo que te mostraba antes:

    <?php
    
    require 'vendor/autoload.php';
    
    use pcrov\JsonReader\JsonReader;
    
    $reader = new JsonReader();
    $reader->open( $argv[1] );
    
    while ($reader->read("name")) {
        echo $reader->value(), "\n";
    }
    $reader->close();

    Al usar el comando

    php process.php categoriesMLB 

    Obtenemos una salida como:

    Acessórios de Carros
    Acessórios para Veículos
    Acessórios de Carros
    Exterior
    Interior
    Comércio
    Agro, Indústria e Comércio
    Comércio
    Contêineres Marítimos
    Insumos
    Máquinas
    Móveis
    Outros
    Acessórios para Veículos
    Acessórios para Veículos
    Acessórios de Carros
    Acessórios de Motos
    Acessórios Náutica
    Ferramentas
    GPS
    Limpeza Automotiva

    Que continúa por unas 1218885 líneas

    Reducir el problema

    Por último, no podemos dejar de lado la solución de «pensamiento lateral» (Que suele ser la mejor :)): re-definir el problema en términos más simples.

    Se trata de hacernos la pregunta: ¿Realmente necesitamos ese archivo? ¿No podríamos trabajar con uno más pequeño?

    Claramente, la respuesta dependerá del contexto más que de cuestiones de tecnología, pero no estaría completo el artículo si no lo mencionara.

    (Si preferís resolver el problema intentando reducir el tamaño del archivo, este artículo puede ser de tu interés).

    Algunas notas finales

    Es importante distinguir entre la necesidad de procesar un archivo tan grande en forma online (Es decir, mientras estamos generando una respuesta para un visitante al sitio) y offline (En un cronjob en la mitad de la noche por ejemplo).

    Claramente, esta herramienta va bien para el segundo caso (Para el primero te diría que si estás con esa necesidad tendrías que re-pensar algo…).

    Por último, no puedo dejar de agradecer a mis amigos y colegas Alejandro Chapiro y Eric Danan por la inspiración para escribir este texto (Y la discusión sobre soluciones que te mostré acá).

  • Cómo pasar datos JSON a CSV usando PHP

    Cómo pasar datos JSON a CSV usando PHP

    La pregunta que dió origen a este post era un poco más amplia:

    Pero como ya hablé de cómo consumir webservices (Sean REST o SOAP) me voy a concentrar en la parte que me llamó la atención: cómo pasar de JSON a CSV.

    Aclaremos los tantos antes de ir a los detalles:

    Qué es JSON

    JSON significa JavaScript Object Notation, es decir: notación de objetos de JavaScript. Está más allá del alcance de este artículo (y de este blog en general) hablar de las bondades (o falta de ellas) de JavaScript… hay mucho material muy bueno al respecto.

    El punto es que, más allá de lo que a vos te guste o no, JavaScript tiene una sintaxis muy práctica para describir los objetos: todo lo que esté encerrado entre {} es un objeto (Con la excepción tal vez del cuerpo de las funciones…).

    Esto lo hace un formato sumamente conveniente para el intercambio de información de estructuras complejas, ya que constituye un modo muy simple de pasar de un objeto en memoria a una representación textual (que puede ser enviada a través de un protocolo de intercambio de textos… por ejemplo HTTP :)).

    En definitiva un texto que se vea así:

    {
       "nombre": "Mauro",
       "amigos": [
          {
             "nombre": "Luis"
          },
          {
             "nombre": "Juan"
          }
       ],
    }

    Estaría representando un objeto con dos propiedades: nombre y amigos. La primera es una cadena, la segunda es un arreglo de otros objetos.

    Más información acá.

    Se ve muy simple (¡y lo es!) pero tiene un pequeño inconveniente: su sintaxis es muy poco permisiva (Dejá una , suelta y agarrate :p), por lo tanto, es muy conveniente usar las funciones propias de PHP para manipularlo (json_encode y json_decode).

    Y acá me da un poco de nostalgia hablar del primer post que escribí para este blog, pero pienso que puede aportar a la respuesta saber cómo iterar sobre un JSON usando PHP.

    Qué es CSV

    CSV significa Comma Separated Values (Valores separados por comas). Se trata de un formato de texto que también es utilizado para el intercambio de información entre sistemas.

    Usualmente el contenido de este tipo de archivos es una sucesión de filas que corresponden con registros cuya cantidad de campos puede variar de uno a otro y la longitud de cada campo también varía.

    Ejemplo:

    Nombre,Apellido,Mail,Edad,Ciudad
    Mauro,Chojrin,mauro.chojrin@leewayweb.com,40,Buenos Aires,
    Steve,Jobs,sjobs@apple.com,,,
    Alfredo,,alopez@gmail.com,,,
    ,Gutierrez,dgutierrez@hotmail.com,46,,

    De JSON a CSV

    Una primera salvedad que hay que hacer es que JSON es un formato que permite estructuras complejas (de múltiples niveles por ejemplo), mientras que CSV es un formato mucho más simple (Se basa en estructuras «planas»), con lo cual, algo se perderá en el camino (o habrá que hacer alguna suposición o transformación de la información).

    A efectos prácticos, asumamos que el JSON de partida es de un único nivel, algo como:

    [
     {
      "nombre": "Mauro",
      "apellido": "Chojrin",
      "correo": "mauro.chojrin@leewayweb.com"
     },
     {
      "nombre": "Pedro",
      "apellido": "Alvarez",
      "correo": "palvarez@gmail.com"
     },
     {
      "nombre": "Laura",
      "apellido": "Perez",
      "correo": "lau.perez@hotmail.com"
     }
    ]

    El CSV correspondiente sería:

    nombre,apellido,correo,
    Mauro,Chojrin,mauro.chojrin@leewayweb.com,
    Pedro,Alvarez,palvarez@gmail.com,
    Laura,Perez,lau.perez@hotmail.com",

    Un código PHP que logra eso es:

    <?php
    
    $f = fopen( 'salida.csv', 'w+' );
    $array = json_decode( file_get_contents( 'entrada.json' ), true );
    
    array_unshift( $array, [ 'nombre', 'apellido', 'correo' ] );
    foreach ( $array as $object ) {
    fputcsv( $f, $object );
    }
    
    fclose( $f );

    Ahora, si la necesidad es más compleja… nada mejor que buscar un poco :).

    Buscando buscando encontré esta herramienta: https://github.com/danmandle/JSON2CSV (No la probé pero parece interesante… ¿te animás a probarla y comentar?)