Tabla de contenidos
JSON es uno de los formatos más populares para transferir datos basados en texto en la web. Está en todas partes y seguramente te lo encontrarás. Le mostraremos cómo manejarlo desde la línea de comandos de Linux usando el jq
comando.
JSON y jq
JSON son las siglas de JavaScript Object Notation . Es un esquema que permite codificar datos en archivos de texto sin formato, de una manera autodescriptiva. No hay comentarios en un archivo JSON; el contenido debe ser autoexplicativo. Cada valor de datos tiene una cadena de texto llamada «nombre» o «clave». Esto le dice cuál es el valor de los datos. Juntos, se conocen como pares nombre: valor o pares clave: valor. Dos puntos ( :
) separan una clave de su valor.
Un «objeto» es una colección de pares clave: valor. En un archivo JSON, un objeto comienza con una llave abierta ( {
) y termina con una llave de cierre ( }
). JSON también admite «matrices», que son listas ordenadas de valores. Una matriz comienza con un corchete de apertura ( [
) y termina con un corchete de cierre ( ]
).
De estas definiciones simples, por supuesto, puede surgir una complejidad arbitraria. Por ejemplo, los objetos se pueden anidar dentro de los objetos. Los objetos pueden contener matrices y las matrices también pueden contener objetos. Todos los cuales pueden tener niveles abiertos de anidación.
En la práctica, sin embargo, si el diseño de los datos JSON es complicado, el diseño del diseño de los datos probablemente debería ser reconsiderado. Por supuesto, si no está generando los datos JSON, simplemente tratando de usarlos, no tiene voz en su diseño. En esos casos, desafortunadamente, solo tienes que lidiar con eso.
La mayoría de los lenguajes de programación tienen bibliotecas o módulos que les permiten analizar datos JSON. Lamentablemente, el shell Bash no tiene esa funcionalidad .
Sin embargo, siendo la necesidad la madre de la invención, jq
nació la utilidad. Con jq
, podemos analizar fácilmente JSON en el shell Bash. Y no importa si tiene que trabajar con JSON elegante y bien diseñado, o con el material de que están hechas las pesadillas.
Cómo instalar jq
Tuvimos que instalarlo jq
en todas las distribuciones de Linux que usamos para investigar este artículo.
Para instalar jq
en Ubuntu, escriba este comando:
sudo apt-get install jq
Para instalar jq
en Fedora, escriba este comando:
sudo dnf instalar jq
Para instalar jq
en Manjaro, escriba este comando:
sudo pacman -Sy jq
Cómo hacer que JSON sea legible
JSON no se preocupa por los espacios en blanco y el diseño no lo afecta. Siempre que siga las reglas de la gramática JSON , los sistemas que procesan JSON pueden leerlo y comprenderlo. Debido a esto, JSON a menudo se transmite como una cadena larga y simple, sin tener en cuenta el diseño. Esto ahorra un poco de espacio porque las pestañas, los espacios y los caracteres de nueva línea no tienen que incluirse en el JSON. Por supuesto, la desventaja de todo esto es cuando un humano intenta leerlo.
Saquemos un objeto JSON corto del sitio de la NASA que nos dice la posición de la Estación Espacial Internacional . Usaremos curl
, que puede descargar archivos para recuperar el objeto JSON por nosotros.
No nos importa ninguno de los mensajes de estado que curl
generalmente se generan, por lo que escribiremos lo siguiente, usando la -s
opción (silenciosa):
curl -s http://api.open-notify.org/iss-now.json
Ahora, con un poco de esfuerzo, puedes leer esto. Tienes que elegir los valores de los datos, pero no es fácil ni conveniente. Repitamos esto, pero esta vez lo pasaremos jq
.
jq
usa filtros para analizar JSON, y el más simple de estos filtros es un punto ( .
), que significa «imprimir el objeto completo». De forma predeterminada, jq
imprime bastante la salida.
Lo juntamos todo y escribimos lo siguiente:
curl -s http://api.open-notify.org/iss-now.json | jq.
¡Eso está mucho mejor! Ahora, podemos ver exactamente lo que está pasando.
Todo el objeto está envuelto en llaves. Contiene dos claves: pares de nombres: message
y timestamp
. También contiene un objeto llamado iss_position
, que contiene dos pares clave: valor: longitude
y latitude
.
Intentaremos esto una vez más. Esta vez escribiremos lo siguiente y redirigiremos la salida a un archivo llamado «iss.json»:
curl -s http://api.open-notify.org/iss-now.json | jq. > iss.json
gato iss.json
Esto nos da una copia bien distribuida del objeto JSON en nuestro disco duro.
Acceder a valores de datos
Como vimos anteriormente, jq
puede extraer valores de datos que se canalizan desde JSON. También puede funcionar con JSON almacenado en un archivo. Vamos a trabajar con archivos locales para que la línea de comandos no esté abarrotada de curl
comandos. Esto debería hacer que sea un poco más fácil de seguir.
La forma más sencilla de extraer datos de un archivo JSON es proporcionar un nombre de clave para obtener su valor de datos. Escriba un punto y el nombre de la clave sin espacios entre ellos. Esto crea un filtro a partir del nombre de la clave. También necesitamos decir jq
qué archivo JSON usar.
Escribimos lo siguiente para recuperar el message
valor:
jq .message iss.json
jq
imprime el texto del message
valor en la ventana de terminal.
Si tiene un nombre de clave que incluye espacios o puntuación, debe envolver su filtro entre comillas. Por lo general, se tiene cuidado de usar caracteres, números y guiones bajos solo para que los nombres de las claves JSON no sean problemáticos.
Primero, escribimos lo siguiente para recuperar el timestamp
valor:
jq .timestamp iss.json
El valor de la marca de tiempo se recupera e imprime en la ventana del terminal.
Pero, ¿cómo podemos acceder a los valores dentro del iss_position
objeto? Podemos usar la notación de puntos JSON. Incluiremos el iss_position
nombre del objeto en la «ruta» al valor de la clave. Para hacer esto, el nombre del objeto en el que se encuentra la clave precederá al nombre de la clave.
Escribimos lo siguiente, incluido el latitude
nombre de la clave (tenga en cuenta que no hay espacios entre «.iss_position» y «.latitude»):
jq .iss_position.latitude iss.json
Para extraer varios valores, debe hacer lo siguiente:
- Enumere los nombres de las teclas en la línea de comando.
- Sepárelos con comas (
,
). - Escríbalos entre comillas (
"
) o apóstrofos ('
).
Con eso en mente, escribimos lo siguiente:
jq ".iss_position.latitude, .timestamp" iss.json
Los dos valores se imprimen en la ventana del terminal.
Trabajar con matrices
Tomemos un objeto JSON diferente de la NASA.
Esta vez, usaremos una lista de los astronautas que están en el espacio ahora mismo :
curl -s http://api.open-notify.org/astros.json
Bien, funcionó, hagámoslo de nuevo.
jq
Escribiremos lo siguiente para canalizarlo y redirigirlo a un archivo llamado «astro.json»:
curl -s http://api.open-notify.org/astros.json | jq. > astro.json
Ahora escriba lo siguiente para verificar nuestro archivo:
menos astro.json
Como se muestra a continuación, ahora vemos la lista de astronautas en el espacio, así como sus naves espaciales.
Este objeto JSON contiene una matriz llamada people
. Sabemos que es una matriz debido al corchete de apertura ( [
) (resaltado en la captura de pantalla anterior). Es una matriz de objetos que cada uno contiene dos pares clave: valor: name
y craft
.
Como hicimos anteriormente, podemos usar la notación de puntos JSON para acceder a los valores. También debemos incluir los corchetes ( []
) en el nombre de la matriz.
Con todo eso en mente, escribimos lo siguiente:
jq ".personas []. nombre" astro.json
Esta vez, todos los valores de nombre se imprimen en la ventana de la terminal. Lo que pedimos jq
hacer fue imprimir el valor del nombre para cada objeto en la matriz. Bastante ordenado, ¿eh?
Podemos recuperar el nombre de un solo objeto si ponemos su posición en la matriz entre corchetes ( []
) en la línea de comando. La matriz utiliza indexación de compensación cero , lo que significa que el objeto en la primera posición de la matriz es cero.
Para acceder al último objeto de la matriz, puede utilizar -1; para obtener el penúltimo objeto de la matriz, puede usar -2, y así sucesivamente.
A veces, el objeto JSON proporciona la cantidad de elementos en la matriz, que es el caso de este. Junto con la matriz, contiene un par clave: nombre llamado number
con un valor de seis.
El siguiente número de objetos están en esta matriz:
jq ".people [1] .name" astro.json
jq ".people [3] .name" astro.json
jq ".people [-1] .name" astro.json
jq ".people [-2] .name" astro.json
También puede proporcionar un objeto inicial y final dentro de la matriz. Esto se llama «rebanar» y puede resultar un poco confuso. Recuerde que la matriz usa un desplazamiento cero.
Para recuperar los objetos desde la posición de índice dos, hasta (pero sin incluir) el objeto en la posición de índice cuatro, escribimos el siguiente comando:
jq ".personas [2: 4]" astro.json
Esto imprime los objetos en el índice de matriz dos (el tercer objeto de la matriz) y tres (el cuarto objeto de la matriz). Detiene el procesamiento en el índice cuatro de la matriz, que es el quinto objeto de la matriz.
La forma de entender mejor esto es experimentar en la línea de comandos. Pronto verás cómo funciona.
Cómo usar tuberías con filtros
Puede canalizar la salida de un filtro a otro y no es necesario que aprenda un nuevo símbolo. Al igual que la línea de comandos de Linux, jq
usa la barra vertical ( |
) para representar una tubería.
Le diremos jq
que canalice la people
matriz al .name
filtro, que debería enumerar los nombres de los astronautas en la ventana de la terminal.
Escribimos lo siguiente:
jq ".people [] | .name" astro.json
Crear matrices y modificar resultados
Podemos utilizar jq
para crear nuevos objetos, como matrices. En este ejemplo, extraeremos tres valores y crearemos una nueva matriz que contenga esos valores. Tenga en cuenta que los [
corchetes de apertura ( ) y de cierre ( ]
) también son el primer y último carácter de la cadena de filtro.
Escribimos lo siguiente:
jq "[.iss-position.latitude, iss_position.longitude, .timestamp]" iss.json
La salida se incluye entre corchetes y se separa por comas, lo que la convierte en una matriz formada correctamente.
Los valores numéricos también se pueden manipular a medida que se recuperan. Extraigamos el timestamp
del archivo de posición de ISS y luego extraigamos de nuevo y cambiemos el valor que se devuelve.
Para hacerlo, escribimos lo siguiente:
jq ".timestamp" iss.json
jq ".timestamp - 1570000000" iss.json
Esto es útil si necesita agregar o eliminar un desplazamiento estándar de una matriz de valores.
Escribamos lo siguiente para recordarnos qué iss.json
contiene el archivo:
jq. iss.json
Digamos que queremos deshacernos del message
par clave: valor. No tiene nada que ver con la posición de la Estación Espacial Internacional. Es solo una bandera que indica que la ubicación se recuperó correctamente. Si excede los requisitos, podemos prescindir de él. (También puede simplemente ignorarlo).
Podemos usar jq
la función de borrar del()
, para borrar un par clave: valor. Para eliminar el mensaje clave: par de valores, escribimos este comando:
jq "del (.message)" iss.json
Tenga en cuenta que esto en realidad no lo elimina del archivo «iss.json»; simplemente lo elimina de la salida del comando. Si necesita crear un nuevo archivo sin el message
par clave: valor en él, ejecute el comando y luego redirija la salida a un nuevo archivo.
Objetos JSON más complicados
Recuperemos más datos de la NASA. Esta vez, usaremos un objeto JSON que contiene información sobre sitios de impacto de meteoritos de todo el mundo. Este es un archivo más grande con una estructura JSON mucho más complicada que los que hemos tratado anteriormente.
Primero, escribiremos lo siguiente para redirigirlo a un archivo llamado «strikes.json»:
curl -s https://data.nasa.gov/resource/y77d-th95.json | jq. > strikes.json
Para ver cómo se ve JSON, escribimos lo siguiente:
menos strikes.json
Como se muestra a continuación, el archivo comienza con un corchete de apertura ( [
), por lo que todo el objeto es una matriz. Los objetos de la matriz son colecciones de pares clave: valor, y hay un objeto anidado llamado geolocation
. El geolocation
objeto contiene más pares clave: valor y una matriz llamada coordinates
.
Recuperemos los nombres de los impactos de meteoritos del objeto en la posición del índice 995 hasta el final de la matriz.
Escribiremos lo siguiente para canalizar el JSON a través de tres filtros:
jq ". [995:] |. [] | .name" strikes.json
Los filtros funcionan de las siguientes formas:
.[995:]
: Esto le dicejq
que procese los objetos desde el índice de matriz 995 hasta el final de la matriz. Ningún número después de los dos puntos (:
) es lo que indicajq
que se continúe hasta el final de la matriz..[]
: Este iterador de matriz indicajq
que se procese cada objeto de la matriz..name
: Este filtro extrae el valor del nombre.
Con un ligero cambio, podemos extraer los últimos 10 objetos de la matriz. Un «-10» indica jq
que se comience a procesar los objetos 10 desde el final de la matriz.
Escribimos lo siguiente:
jq ". [- 10:] |. [] | .name" strikes.json
Al igual que en los ejemplos anteriores, podemos escribir lo siguiente para seleccionar un solo objeto:
jq ". [650] .name" strikes.json
También podemos aplicar el corte a las cuerdas. Para hacerlo, escribiremos lo siguiente para solicitar los primeros cuatro caracteres del nombre del objeto en el índice de matriz 234:
jq ". [234] .name [0: 4]" strikes.json
También podemos ver un objeto específico en su totalidad. Para hacer esto, escribimos lo siguiente e incluimos un índice de matriz sin ninguna clave: filtros de valor:
jq ". [234]" strikes.json
Si desea ver solo los valores, puede hacer lo mismo sin los nombres de las claves.
Para nuestro ejemplo, escribimos este comando:
jq ". [234] []" strikes.json
Para recuperar varios valores de cada objeto, los separamos con comas en el siguiente comando:
jq ". [450: 455] |. [] | .name, .mass" strikes.json
Si desea recuperar valores anidados, debe identificar los objetos que forman la «ruta» hacia ellos.
Por ejemplo, para hacer referencia a los coordinates
valores, tenemos que incluir la matriz que lo abarca todo, el geolocation
objeto anidado y la coordinates
matriz anidada , como se muestra a continuación.
Para ver los coordinates
valores del objeto en la posición de índice 121 de la matriz, escribimos el siguiente comando:
jq ". [121] .geolocation.coordinates []" strikes.json
La función de longitud
La jq
length
función da distintas métricas según lo que se haya aplicado, como por ejemplo:
- Cadenas : la longitud de la cadena en bytes.
- Objetos : el número de pares clave: valor en el objeto.
- Matrices : el número de elementos de matriz en la matriz.
El siguiente comando devuelve la longitud del name
valor en 10 de los objetos de la matriz JSON, comenzando en la posición del índice 100:
jq ". [100: 110] |. []. nombre | longitud" strikes.json
Para ver cuántos pares clave: valor hay en el primer objeto de la matriz, escribimos este comando:
jq ". [0] | longitud" strikes.json
Las teclas Función
Puede usar la función de teclas para averiguar sobre el JSON con el que debe trabajar. Puede decirle cuáles son los nombres de las claves y cuántos objetos hay en una matriz.
Para encontrar las claves en el people
objeto en el archivo «astro.json», escribimos este comando:
jq ".personas. [0] | claves" astro.json
Para ver cuántos elementos hay en la people
matriz, escribimos este comando:
jq ".people | keys" astro.json
Esto muestra que hay seis elementos de matriz de compensación cero, numerados del cero al cinco.
La función has ()
Puede usar la has()
función para interrogar al JSON y ver si un objeto tiene un nombre de clave en particular. Tenga en cuenta que el nombre de la clave debe estar entre comillas. Envolveremos el comando de filtro entre comillas simples ( '
), de la siguiente manera:
jq '. [] | tiene ("nametype") 'strikes.json
Cada objeto de la matriz está marcado, como se muestra a continuación.
Si desea verificar un objeto específico, incluya su posición de índice en el filtro de matriz, de la siguiente manera:
jq '. [678] | tiene ("nametype") 'strikes.json
No te acerques a JSON sin él
La jq
utilidad es el ejemplo perfecto del software profesional, potente y rápido que hace que vivir en el mundo Linux sea un placer.
Esta fue solo una breve introducción a las funciones comunes de este comando; hay mucho más. Asegúrese de consultar el manual completo de jq si desea profundizar más.