sqlpostgresqljsonjsonb

JSONB_BUILD_OBJECT no PostgreSQL: montar um objeto JSON a partir de colunas com tipos

Monte um objeto JSON a partir de pares chave/valor com tipos preservados, modele payloads por linha, aninhe objetos e arrays e compare com to_jsonb.

3 min de leituraReferencesql · postgresql · json · jsonb · api

jsonb_build_object monta um objeto JSON dentro da propria consulta a partir de argumentos alternados chave, valor, chave, valor, .... Ao contrario da concatenacao manual de strings, ela preserva os tipos dos valores: um numero continua numero, um booleano continua booleano e NULL vira um null JSON de verdade. E a ferramenta de trabalho para devolver um payload de API pronto ou uma estrutura aninhada em um unico SELECT.

Sintaxe e exemplo basico

Os argumentos vem em pares: as posicoes impares sao chaves (convertidas para texto) e as pares sao valores de qualquer tipo. O resultado e jsonb; o irmao json_build_object devolve json (texto sem normalizacao).

SELECT jsonb_build_object(
    'id', id,
    'email', email,
    'country', country
) AS payload
FROM users;

Alguns comportamentos que vale conhecer:

  • O numero de argumentos deve ser par, ou ocorre o erro argument list must have even number of elements.
  • Os tipos dos valores sao preservados: amount numeric vira um numero JSON, nao uma string entre aspas.
  • Um valor NULL produz um null JSON, e a chave permanece no objeto.
  • Um NULL em posicao de chave e rejeitado com erro; as chaves sempre precisam estar presentes.

Um payload de API por linha

O caso principal e modelar um payload de API dentro do banco sem duplicar nomes de campos no codigo da aplicacao. Montar um "cartao" de usuario com alguns campos derivados e natural:

SELECT jsonb_build_object(
    'user_id', u.id,
    'name', u.name,
    'is_verified', (u.email IS NOT NULL),
    'signup_year', EXTRACT(YEAR FROM u.created_at)
) AS user_card
FROM users u;

Aqui is_verified e um booleano JSON real, nao a string "true". Essa e a vantagem chave sobre a concatenacao: os tipos sobrevivem e o PostgreSQL cuida das aspas e do escape por voce.

Pegadinha: as chaves nao sao deduplicadas na construcao em json_build_object (o tipo json), mas o jsonb mantem o ultimo valor de uma chave repetida e descarta o resto. Se voce informar uma chave duas vezes, o resultado jsonb guarda apenas o par final, entao a ordem dos argumentos decide qual vence.

Aninhamento: objetos dentro de objetos e arrays

jsonb_build_object se aninha livremente dentro de si mesmo e dentro de jsonb_build_array, entao voce pode construir estruturas de qualquer profundidade, como um pedido com um bloco de cliente aninhado e uma lista de tags:

SELECT jsonb_build_object(
    'order_id', o.id,
    'amount', o.amount,
    'status', o.status,
    'customer', jsonb_build_object(
        'id', u.id,
        'email', u.email
    ),
    'flags', jsonb_build_array('priority', o.status)
) AS order_json
FROM orders o
JOIN users u ON u.id = o.user_id;

Para construir um array de objetos filhos (todos os pedidos de um usuario), combine jsonb_build_object com o agregado jsonb_agg:

SELECT jsonb_build_object(
    'user_id', u.id,
    'orders', jsonb_agg(
        jsonb_build_object('id', o.id, 'amount', o.amount)
        ORDER BY o.created_at
    )
) AS user_with_orders
FROM users u
JOIN orders o ON o.user_id = u.id
GROUP BY u.id;

Quando usar to_jsonb em vez disso

Quando voce quer JSON com todas as colunas de uma linha e sem renomear, to_jsonb(row) e mais simples: ele mapeia a linha pelo nome da coluna automaticamente.

SELECT to_jsonb(e) AS employee_json
FROM employees e;

Escolher entre os dois e direto:

  • to_jsonb(t) e um dump rapido da linha inteira; as chaves sao os nomes das colunas e voce nao pode escolher nem renomear campos.
  • jsonb_build_object(...) da controle total sobre o conjunto de chaves, os nomes e a ordem, ideal para um contrato de API estavel.
  • Hibrido: to_jsonb(e) - 'salary' remove um campo, e to_jsonb(e) || jsonb_build_object('bonus', e.salary * 0.1) adiciona um calculado.

MySQL e ClickHouse

O MySQL oferece JSON_OBJECT('id', id, 'name', name) com a mesma logica de pares alternados, e os tipos dos valores tambem sao preservados. Nao ha um equivalente direto de to_jsonb(row), entao voce lista as chaves na mao. Para arrays de objetos, use JSON_ARRAYAGG(JSON_OBJECT(...)).

O ClickHouse trabalhou historicamente com JSON em string por meio de funcoes como toJSONString sobre tuplas e tuples nomeadas. Quando voce precisa de um contrato de resposta estrito com tipos e aninhamento, o PostgreSQL com jsonb_build_object mais jsonb_agg continua sendo a ferramenta mais direta e previsivel.

Pratique com exercícios reais

Resolva exercícios no treinador de SQL com correção instantânea e dicas.

Abrir o treinador