Asumiremos que ya conoces la sintaxis y los tipos de datos de JSON.
jq funciona pasando los datos JSON de entrada por una sola expresión (escrita como una tubería de filtros) para obtener los datos transformados que buscas.
El lenguaje jq está implementado por el programa jq.
Este programa ofrece varias opciones de línea de comandos muy útiles para controlar cómo se consume la entrada y cómo se presenta la salida.
En los ejemplos de abajo encontrarás:
-n o --null-input
Normalmente al programa jq se le da un archivo para leer, o le envías datos a su entrada.
La opción --null-input te permite generar datos JSON sin ninguna entrada.
-c o --compact-output
jq imprime su salida con un formato bonito por defecto.
Es muy útil para las personas ver los datos cuando están bien formateados.
Sin embargo, eso no es necesario para las máquinas: la opción --compact-output elimina los espacios en blanco del formato para minimizar el tamaño del JSON resultante.
-f filename o --from-file filename
Lee el programa jq desde filename en lugar de darlo en la línea de comandos.
sed y awk usan la opción -f para el mismo propósito.
Verás esto en los scripts de prueba de los ejercicios de práctica.
El resto de esta lección se centrará en el lenguaje jq.
Los filtros también se conocen como expresiones.
Un filtro toma una entrada y produce una salida.
Igual que cuando trabajas en una shell de Unix, puedes unir filtros con una tubería | para conectar la salida de un filtro con la entrada de otro.
.
Este es el filtro más simple. Simplemente pasa su entrada a su salida.
$ echo '[1, 2, 3]' | jq '.'
[
1,
2,
3
]
Esta será una introducción rápida al trabajo con arrays. Cubriremos este tema con mucho más detalle más adelante.
Se accede a los elementos de un array con corchetes, y su índice empieza en cero.
$ echo '[10, 20, 30]' | jq '.[1]'
20
Un filtro puede construir un array envolviendo una expresión en [ y ]
con una lista conocida de elementos:
jq -n '[1, 2, 3]'
para recolectar un flujo de elementos: por ejemplo,
range es una función que produce un flujo de números
$ jq -n 'range(10; 70; 15)'
10
25
40
55
Usar [] recolecta los resultados de la expresión en un array
$ jq -c -n '[range(10; 70; 15)]'
[10,25,40,55]
La coma no es solo la sintaxis que separa los elementos de un array. La coma es un operador que une flujos.
Por ejemplo, [1, 2, 3] es un filtro que usa el constructor de array [] para recolectar el resultado de unir las tres expresiones 1, 2 y 3.
¿Notaste los puntos y coma en range(10; 70; 15) de arriba?
Como las comas tienen un propósito específico en el lenguaje jq, las funciones que toman varios argumentos usan puntos y coma para separar los argumentos.
Una introducción rápida a los objetos.
Igual que en muchos lenguajes de programación, usa puntos para acceder a las propiedades de un objeto
$ echo '{"foo": {"bar": "qux"}}' | jq '.foo.bar'
"qux"
Los corchetes también se pueden usar para los objetos, pero en ese caso se necesitan comillas para los literales de string. Este es uno de los métodos para trabajar con claves que contienen espacios.
$ echo '{"foo bar": "qux"}' | jq '.["foo bar"]'
"qux"
Puedes construir un objeto con {} y pares key: value.
No se necesitan comillas alrededor de las claves que son strings "simples".
jq -n '{question: (6 * 9), answer: 42}'
produce
{
"question": 54,
"answer": 42
}
Para tratar la clave como una expresión, debes envolverla entre paréntesis (lo siguiente también produce la misma salida que arriba).
echo '[{"key":"question", "value":54}, {"key":"answer", "value":42}]' \
| jq '{(.[0].key): .[0].value, (.[1].key): .[1].value}'
Por ejemplo, dado file.json con
{
"key1": "value1",
"key2": [5, 15, 25]
}
Calculemos la longitud del array key2:
$ jq '.key2 | length' file.json
3
Estamos usando una tubería: la salida de la expresión .key2 entra como la entrada a length, que, como era de esperar, produce el número de elementos del array.
En este ejemplo, los datos JSON de entrada se ignoran y no tienen ningún efecto en la salida:
$ echo '{"answer": 42}' | jq '6 * 9'
54
Un filtro puede producir más de un valor.
Por ejemplo, el filtro .[] produce cada elemento de un array como un valor separado:
$ jq -n -c '[1, 2, 3]'
[1,2,3]
$ jq -n -c '[1, 2, 3] | .[]'
1
2
3
Pasar un filtro así por una tubería hacia otro ejecutará el segundo filtro para cada valor:
$ jq -n -c '[1, 2, 3] | .[] | . * 2'
2
4
6
Esto es como una iteración implícita.
Una vez que entiendas esta técnica, te darás cuenta de que los filtros de jq pueden ser muy potentes y a la vez muy concisos.
Los paréntesis se usan para agrupar subexpresiones y así forzar el orden de las operaciones, igual que en otros lenguajes.
En jq, la necesidad de usarlos puede resultar un poco sorprendente.
Por ejemplo, supongamos que queremos construir un array con 2 elementos: la raíz cuadrada de 9, y e elevado a la potencia 1.
Las dos expresiones individuales son 9 | sqrt y 1 | exp.
Esperamos que la salida sea el array [3, 2.7...]
$ jq -n '[ 9|sqrt, 1|exp ]'
[
20.085536923187668,
2.718281828459045
]
¿Por qué no obtuvimos lo que esperábamos? jq lo interpreta así:
[ ((9|sqrt), 1) | exp ]
jq construye un flujo de dos elementos (3 y 1) y cada uno de ellos se le pasa a exp.
Necesitamos asegurarnos de que exp reciba solo un número como entrada.
En otras palabras, necesitamos forzar que la tubería se evalúe antes que la coma.
$ jq -n '[ 9|sqrt, (1|exp) ]'
[
3,
2.718281828459045
]
Sin entrar en demasiada profundidad (las funciones serán tema de otro ejercicio), aquí tienes algunas funciones integradas útiles:
length
Dado un array como entrada, produce el número de elementos del array.
$ jq -n '[10, 20, 30, 40] | length'
4
+
Este operador hace cosas distintas según el tipo de sus operandos: suma números, concatena strings, agrega arrays, combina objetos.
$ jq -c -n '
3 + 4,
"foo" + "bar",
["a", "b"] + ["c"],
{"m": 10} + {"n": 20}
'
7
"foobar"
["a","b","c"]
{"m":10,"n":20}
add es una función que toma un array y devuelve un elemento con todos los elementos sumados entre sí según las reglas de +.
[1, 2, 3] | add produce 6.
map
Dado un array como entrada y un filtro como argumento, produce un array donde el filtro se aplica a cada elemento
$ jq -c -n '[10, 20, 30, 40] | map(. / 5)'
[2,4,6,8]
select
Dada alguna entrada y un filtro como argumento:
null, de verdad no produce salida)Por ejemplo, dados algunos números, selecciona los que son divisibles por 3
$ jq -n 'range(10) | select(. % 3 == 0)'
0
3
6
9
Recuerda que range produce un flujo de números.
select se invocará una vez por cada número.
Solo se producen los números que «pasan» la expresión.
A menudo necesitas seleccionar elementos de un array. Hay un par de formas de hacerlo.
Con la entrada ["Anne", "Bob", "Cathy", "Dave"], selecciona los nombres que tienen longitud 4.
usa map y select juntos
map(select(length == 4))
separa el array en elementos, aplica select a ese flujo y recolecta los resultados
[ .[] | select(length == 4) ]
Los comentarios empiezan con el carácter # y continúan hasta el final de la línea.
De repente te dan unas ganas enormes de comer panqueques. Como una persona moderna con inclinación por la tecnología, escribes una lista de compras de los ingredientes para panqueques en formato JSON. La estructura de la lista es:
{
"name": "name of shopping list",
"ingredients": [ ...list of ingredients... ],
"optional ingredients": [ ...list of ingredients... ]
}
Un ingrediente se representa como un objeto JSON, así:
{
"item": "flour",
"amount": {
"quantity": 2,
"unit": "cup"
}
}
Un ingrediente tiene una propiedad opcional llamada "substitute" que contiene un string con otro ingrediente que se puede usar en su lugar.
Veamos cómo podemos examinar la lista de compras con jq
Escribe una expresión que devuelva el elemento "name" de la lista de compras.
Cuenta solo los ingredientes "required". No incluyas los ingredientes opcionales.
Escribe una expresión que devuelva la cantidad de azúcar. Solo se quiere la parte numérica.
Algunos de los ingredientes se pueden sustituir (si no tienes el ingrediente X puedes usar Y). Devuelve un objeto JSON que mapee el ingrediente recomendado a su sustitución. Incluye los ingredientes opcionales en el mapeo.
Regístrate en Exercism para aprender y dominar jq con 12 conceptos75 ejercicios y mentoría humana real, todo gratis.