sqlpostgresqljsonbjson

to_jsonb en PostgreSQL: convertir una fila entera en un objeto JSON

Como to_jsonb convierte cualquier valor, fila o array en jsonb conservando los tipos, y en que se diferencia de json_build_object y row_to_json.

3 min de lecturaReferencesql · postgresql · jsonb · json · row_to_json

Cuando necesitas exponer una fila de una tabla como JSON o armar una estructura anidada para una API, PostgreSQL ofrece to_jsonb. Toma cualquier valor SQL, fila o array y lo convierte en jsonb sin perder los tipos. Veamos como usarla y en que se diferencia de json_build_object y row_to_json.

Que hace to_jsonb

to_jsonb(value) convierte un unico valor en jsonb. Los escalares se vuelven escalares JSON, los arrays se vuelven arrays JSON y una fila de tabla se vuelve un objeto JSON cuyas claves son los nombres de columna:

-- Scalars and arrays keep their natural JSON shape
SELECT to_jsonb(42)                AS num,
       to_jsonb('hello'::text)     AS str,
       to_jsonb(ARRAY[1, 2, 3])    AS arr,
       to_jsonb(true)              AS flag;

El truco mas util es pasar una fila entera. Un alias de tabla dentro de to_jsonb se refiere a toda la fila:

-- A whole row becomes one JSON object: column -> value
SELECT to_jsonb(u) AS user_json
FROM users u
WHERE u.country = 'DE';

Cada fila sale como {"id": 7, "email": "a@b.de", "name": "Anna", "country": "DE", "created_at": "2026-01-10T08:00:00"}. Fijate en que created_at se serializa como cadena ISO, los numeros siguen siendo numeros y NULL se vuelve null de JSON.

Los tipos se conservan, y eso importa

to_jsonb respeta los tipos de origen. Un entero sigue siendo numero, no cadena; un booleano sigue siendo un true/false real. Eso la diferencia de concatenar texto a mano:

SELECT jsonb_typeof(to_jsonb(o.amount))  AS amount_kind,   -- number
       jsonb_typeof(to_jsonb(o.status))  AS status_kind,   -- string
       jsonb_typeof(to_jsonb(NULL::int)) AS null_kind       -- null
FROM orders o
LIMIT 1;
  • integer y numeric se vuelven numeros JSON.
  • text, varchar, date, timestamp se serializan como cadenas.
  • boolean sigue siendo booleano.
  • Los arrays de PostgreSQL se vuelven arrays JSON, conservando el anidamiento.

Gotcha: un timestamp sin zona horaria se convierte sin sufijo Z, mientras que timestamptz incluye el desfase. Si el frontend espera UTC estricto, convierte antes la columna a timestamptz.

Objetos anidados y seleccion de columnas

A menudo no quieres la fila completa: quieres elegir campos o anidar datos relacionados. Hay dos caminos. El primero: construir el objeto desde la fila y quitar claves sobrantes con -:

-- Drop sensitive or noisy keys from the row object
SELECT to_jsonb(u) - 'created_at' - 'id' AS public_user
FROM users u;

El segundo, mucho mas comun en la practica, es anidar los pedidos de un usuario como un array. jsonb_agg agrega las filas de pedidos, cada una convertida en objeto con to_jsonb:

-- Nest each user's orders as an array of JSON objects
SELECT to_jsonb(u) || jsonb_build_object(
         'orders',
         (SELECT jsonb_agg(to_jsonb(o))
          FROM orders o
          WHERE o.user_id = u.id)
       ) AS user_with_orders
FROM users u;

El operador || fusiona dos objetos jsonb, anadiendo la clave orders al objeto del usuario. Asi se construye un arbol de cualquier profundidad.

to_jsonb frente a json_build_object

Cuando necesitas una forma explicita -- otros nombres de clave, campos calculados, un orden fijo -- usa json_build_object (o jsonb_build_object). Enumeras los pares clave-valor a mano:

-- Explicit shape: rename keys and add a computed field
SELECT jsonb_build_object(
         'user_id',   u.id,
         'label',     u.name || ' <' || u.email || '>',
         'is_local',  u.country = 'DE'
       ) AS card
FROM users u;

Como elegir:

  • to_jsonb(row) -- cuando quieres el objeto tal cual, con nombres de columna. Codigo minimo, la forma sigue al esquema.
  • jsonb_build_object(...) -- cuando necesitas una forma exacta: renombrar, calcular, seleccionar campos, orden de claves estable.

Suelen combinarse: construyes la base con to_jsonb y luego anades claves calculadas encima con || y jsonb_build_object.

to_jsonb frente a row_to_json

La funcion antigua row_to_json(row) hace casi lo mismo, pero devuelve el tipo json, no jsonb. La diferencia es de fondo. Los operadores de acceso ->, ->>, #> y #>> funcionan tanto sobre json como sobre jsonb, asi que leer una clave no es lo que los distingue. Lo que json no puede hacer es el resto de la caja de herramientas:

  • json guarda el texto literal: conserva el orden de claves, los duplicados y los espacios, pero no admite los operadores -, ||, @> (exclusivos de jsonb) ni la indexacion GIN.
  • jsonb guarda una forma binaria analizada: se pierde el orden de claves, los duplicados se colapsan, pero tienes esos operadores extra y busquedas indexadas rapidas.
-- Both json and jsonb support -> ; the jsonb-only operators are -, ||, @>
SELECT row_to_json(e)            AS as_json,
       to_jsonb(e)               AS as_jsonb,
       to_jsonb(e) -> 'salary'   AS salary_node   -- -> works on json too
FROM employees e
WHERE e.dept = 'eng';

Regla practica: para codigo nuevo usa to_jsonb, porque jsonb se manipula con los operadores extra, se indexa con GIN y es mas rapido de consultar. Reserva row_to_json para los casos donde importa la salida textual exacta.

En MySQL el analogo mas cercano es JSON_OBJECT('k', v, ...), parecido a jsonb_build_object; no hay un "fila entera a JSON" directo, asi que enumeras las claves a mano. En ClickHouse el modelo JSON es distinto: tiene un tipo JSON y una funcion toJSONString, pero la semantica de "fila como objeto" difiere, asi que las consultas no se trasladan una a una.

Practica con ejercicios reales

Resuelve ejercicios en el entrenador de SQL con corrección instantánea y pistas.

Abrir el entrenador