JSON es un formato muy útil para inter operar entre sistemas, y es por eso que se usa tanto en las API. Pero a veces es más sencillo usar otros formatos. O queremos hacer análisis de datos de una salida que está en JSON.
Un formato muy útil y común para hacer análisis de datos es CSV, o “Comma Separated Values” (Valores separados por una coma).
JQ
tiene soporte para este formato y vamos a ver cómo lo podemos usar con un simple ejemplo.
Volvamos a usar la API que exploramos en el artículo anterior de esta serie: SWAPI, que nos entrega datos sobre las películas de Star Wars.
Esta vez vamos a tomar la lista de naves y vamos a seleccionar su nombre, el largo de la misma, la cantidad de tripulantes y en cuantas películas sale.
Lo primero que podemos hacer es obtener esos atributos de nuestro arreglo de resultados (que como vimos antes, viene en el arreglo results
).
Para hacer esto ejecutamos este comando:
curl https://swapi.dev/api/starships | jq -r '.results[] | [.name, .length, .crew, .films]'
La salida de esto es:
[
"CR90 corvette",
"150",
"30-165",
[
"https://swapi.dev/api/films/1/",
"https://swapi.dev/api/films/3/",
"https://swapi.dev/api/films/6/"
]
]
[
"Star Destroyer",
"1,600",
"47,060",
[
"https://swapi.dev/api/films/1/",
"https://swapi.dev/api/films/2/",
"https://swapi.dev/api/films/3/"
]
]
[
"Sentinel-class landing craft",
"38",
"5",
[
"https://swapi.dev/api/films/1/"
]
]
...
(Como siempre, he recortado toda la salida)
Notarán que he decidido mostrar la salida como arreglos, esto porque es la manera más útil para transformar a CSV.
Pero el atributo films
no nos entrega un número, quisiéramos contar los films, más que tener la lista de estos, para eso debemos calcular el largo del atributo films
de este modo:
curl https://swapi.dev/api/starships | jq -r '.results[] | [.name, .length, .crew, (.films | length)]'
Noten que debemos poner entre paréntesis el filtro (.films | length)
. Te animo a investigar qué pasa si omites los paréntesis. Otra cosa es que length
es un filtro predefinido en jq
, por eso que debe ser invocado de ese modo. No confundir con el atributo length
, que se designa .length
(con el punto como prefijo).
El resultado de esto es:
[
"CR90 corvette",
"150",
"30-165",
3
]
[
"Star Destroyer",
"1,600",
"47,060",
3
]
[
"Sentinel-class landing craft",
"38",
"5",
1
]
Para convertir a CSV existe el filtro @csv
y se aplica así:
curl https://swapi.dev/api/starships | jq -r '.results[] | [.name, .length, .crew, (.films | length)] | @csv'
Con lo que obtenemos:
"CR90 corvette","150","30-165",3
"Star Destroyer","1,600","47,060",3
"Sentinel-class landing craft","38","5",1
"Death Star","120000","342,953",1
"Millennium Falcon","34.37","4",3
"Y-wing","14","2",3
"X-wing","12.5","1",3
"TIE Advanced x1","9.2","1",1
"Executor","19000","279,144",2
"Rebel transport","90","6",2
Noten que por lo compacto de esta representación pude colocar todos los resultados esta vez.
Otra cosa es que he invocado jq
con el parámetro -r
(por raw output). La razón de esto es que la salida de @csv es un string, y si no usamos este parámetro, la salida se vería así:
"\"CR90 corvette\",\"150\",\"30-165\",3"
"\"Star Destroyer\",\"1,600\",\"47,060\",3"
"\"Sentinel-class landing craft\",\"38\",\"5\",1"
"\"Death Star\",\"120000\",\"342,953\",1"
"\"Millennium Falcon\",\"34.37\",\"4\",3"
"\"Y-wing\",\"14\",\"2\",3"
"\"X-wing\",\"12.5\",\"1\",3"
"\"TIE Advanced x1\",\"9.2\",\"1\",1"
"\"Executor\",\"19000\",\"279,144\",2"
"\"Rebel transport\",\"90\",\"6\",2"
Que no es lo que queremos.
Te dejo planteado como ejercicio agregarle los encabezados a la salida.
Si te gusta este contenido y lo encuentras útil te invito a suscribirte
Y si ya estás suscrito y piensas que esto es útil para algún colega, te invito a compartirlo:
Gracias por tu tiempo, y seguiremos la próxima semana explorando jq
y otros mini lenguajes.