kb.erickguedes.com
JSON: Formato e Manipulação

JSONPath e jq

Aula 3 de 4

JSONPath — Consultas em JSON

JSONPath é uma linguagem de consulta para navegar e extrair dados de documentos JSON, similar ao XPath para XML.

Expressões Básicas

ExpressãoDescrição
$.usuariosNavega para a chave usuarios
$..nomeBusca recursiva por nome
$.usuarios[0]Primeiro elemento do array
$.usuarios[*].nomeNomes de todos os usuários
$[?(@.idade > 30)]Filtro condicional
$[?(@.tags[*] == 'admin')]Filtro por array interno

Exemplos Práticos

{
  "loja": "Tech Store",
  "produtos": [
    {
      "nome": "Notebook",
      "preco": 3500,
      "categoria": "Eletrônicos",
      "tags": ["promocao", "destaque"]
    },
    {
      "nome": "Mouse",
      "preco": 50,
      "categoria": "Periféricos",
      "tags": ["novo"]
    },
    {
      "nome": "Monitor",
      "preco": 1500,
      "categoria": "Eletrônicos",
      "tags": ["destaque"]
    }
  ]
}
# Navegação básica
$.produtos[*].nome
# Resultado: ["Notebook", "Mouse", "Monitor"]

# Filtro por preço
$.produtos[?(@.preco > 1000)].nome
# Resultado: ["Notebook", "Monitor"]

# Busca recursiva
$..nome
# Resultado: ["Tech Store", "Notebook", "Mouse", "Monitor"]

jq — Processador JSON para CLI

jq é uma ferramenta de linha de comando poderosa para processar JSON.

Filtros Básicos

# Extrair campos
cat dados.json | jq '.produtos[].nome'

# Objeto formatado
cat dados.json | jq '{nome: .produtos[].nome, preco: .produtos[].preco}'

# Selecionar itens
cat dados.json | jq '.produtos[] | select(.preco > 100)'

# Array de campos específicos
cat dados.json | jq '[.produtos[] | {nome, preco}]'

Pipe e Transformações

# Pipeline de transformação
curl -s https://api.github.com/repos/stedolan/jq/commits | \
  jq '[.[] | {sha: .sha[0:7], autor: .commit.author.name, msg: .commit.message | split("\n")[0]}]'

# Agregações
jq '{total: [.produtos[].preco] | add, media: [.produtos[].preco] | avg, max: [.produtos[].preco] | max}' dados.json

# Map e Select
cat dados.json | jq '.produtos | map(select(.preco > 100)) | map(.nome)'

Manipulação em Pipelines Bash

# Extrair API JSON e processar
curl -s "https://api.github.com/repos/jqlang/jq/issues?state=open" | \
  jq 'group_by(.labels[].name) | map({label: .[0].labels[0].name, count: length})'

# Transformar JSON para CSV
curl -s "https://api.exemplo.com/usuarios" | \
  jq -r '.[] | [.id, .nome, .email] | @csv' > usuarios.csv

# Atualizar valores em JSON
cat config.json | jq '.database.host = "db-novo.exemplo.com" | .database.port = 5432' > config-novo.json

# Merge de arquivos JSON
jq -s '{usuario: .[0], config: .[1]}' usuario.json config.json

# JSON pretty-print
curl -s https://api.exemplo.com/dados | jq .

Lab: Pipeline de Dados com jq

# 1. Arquivo de dados
cat << 'EOF' > vendas.json
{
  "periodo": "2024-Q1",
  "vendas": [
    {"vendedor": "Ana", "valor": 15000, "mes": "janeiro", "regiao": "SP"},
    {"vendedor": "Carlos", "valor": 22000, "mes": "janeiro", "regiao": "RJ"},
    {"vendedor": "Ana", "valor": 18000, "mes": "fevereiro", "regiao": "SP"},
    {"vendedor": "Beatriz", "valor": 12000, "mes": "fevereiro", "regiao": "MG"},
    {"vendedor": "Carlos", "valor": 25000, "mes": "marco", "regiao": "RJ"},
    {"vendedor": "Ana", "valor": 20000, "mes": "marco", "regiao": "SP"}
  ]
}
EOF

# 2. Análises com jq
echo "=== Total por vendedor ==="
jq '[.vendas[] | {vendedor, valor}] | group_by(.vendedor) | map({vendedor: .[0].vendedor, total: [.[].valor] | add})' vendas.json

echo ""
echo "=== Total por região ==="
jq '[.vendas[] | {regiao, valor}] | group_by(.regiao) | map({regiao: .[0].regiao, total: [.[].valor] | add})' vendas.json

echo ""
echo "=== Vendedor do mês (maior venda única) ==="
jq '.vendas | max_by(.valor) | {vendedor, valor, mes}' vendas.json

echo ""
echo "=== Média de vendas ==="
jq '[.vendas[].valor] | avg' vendas.json

echo ""
echo "=== Exportar CSV ==="
jq -r '.vendas[] | [.vendedor, .valor | tostring, .mes, .regiao] | @csv' vendas.json

# 3. JSONPath com Python
python -c "
import json
from jsonpath_ng import parse

with open('vendas.json') as f:
    dados = json.load(f)

# Parse JSONPath expression
expr = parse('\$.vendas[?(@.regiao == \"SP\")].vendedor')
matches = [m.value for m in expr.find(dados)]
print(f'Vendedores de SP: {matches}')

expr = parse('\$.vendas[?(@.valor > 15000)].vendedor')
matches = [m.value for m in expr.find(dados)]
print(f'Vendas > 15000: {matches}')
"

jq transforma o terminal em um poderoso ambiente ETL para JSON — sem precisar de Python ou JavaScript.