JSON_OBJECT

La función JSON_OBJECT construye un objeto JSON a partir de pares clave-valor. Es la forma recomendada de crear objetos JSON en MySQL porque garantiza un formato válido, escapa automáticamente los caracteres especiales en las claves y los valores, y realiza las conversiones de tipos SQL a tipos JSON de forma correcta. Cada par de argumentos se convierte en una entrada "clave": valor del objeto resultante.

Sintaxis

JSON_OBJECT(clave1, valor1, clave2, valor2, ..., claveN, valorN)

Los argumentos van siempre en pares: el primero es la clave (que debe ser una cadena) y el segundo es el valor (que puede ser de cualquier tipo). Si proporcionas un número impar de argumentos, MySQL devuelve un error. Sin argumentos, la función crea un objeto vacío.

Comportamiento básico

Crear un objeto con diferentes tipos de valores:

SELECT JSON_OBJECT(
    'nombre', 'Monitor LG 4K',
    'precio', 349.99,
    'en_stock', TRUE,
    'descuento', NULL
) AS producto;
producto
{"nombre": "Monitor LG 4K", "precio": 349.99, "en_stock": true, "descuento": null}

Cada tipo SQL se convierte a su equivalente JSON: las cadenas se entrecomillan, los números se mantienen, los booleanos se convierten a true/false, y NULL se convierte al literal null de JSON.

Un objeto vacío se crea llamando a la función sin argumentos:

SELECT JSON_OBJECT() AS vacio;
vacio
{}

Objetos anidados

Para crear objetos dentro de otros objetos, anida llamadas a JSON_OBJECT:

SELECT JSON_OBJECT(
    'producto', 'Portátil Gaming',
    'precio', 1299.00,
    'especificaciones', JSON_OBJECT(
        'procesador', 'AMD Ryzen 9 7945HX',
        'ram', '32GB DDR5',
        'gpu', 'RTX 4070'
    ),
    'dimensiones', JSON_OBJECT(
        'ancho', 35.6,
        'profundidad', 23.4,
        'alto', 2.26,
        'unidad', 'cm'
    )
) AS detalle;
detalle
{"producto": "Portátil Gaming", "precio": 1299.00, "especificaciones": {"procesador": "AMD Ryzen 9 7945HX", "ram": "32GB DDR5", "gpu": "RTX 4070"}, "dimensiones": {"ancho": 35.6, "profundidad": 23.4, "alto": 2.26, "unidad": "cm"}}

MySQL reconoce que los valores internos ya son de tipo JSON y los integra correctamente en la estructura, sin convertirlos a cadenas.

Caso práctico: transformar filas en objetos JSON

Uno de los usos más potentes de JSON_OBJECT es convertir datos relacionales en documentos JSON para enviar a una aplicación:

CREATE TABLE clientes (
    id INT AUTO_INCREMENT PRIMARY KEY,
    nombre VARCHAR(100),
    email VARCHAR(150),
    ciudad VARCHAR(100),
    fecha_registro DATE
);
 
INSERT INTO clientes (nombre, email, ciudad, fecha_registro) VALUES
('Laura Martínez', 'laura@example.com', 'Madrid', '2023-06-15'),
('Diego Fernández', 'diego@example.com', 'Barcelona', '2023-09-22'),
('Sofía Castro', 'sofia@example.com', 'Valencia', '2024-01-10');
SELECT JSON_OBJECT(
    'id', id,
    'nombre', nombre,
    'email', email,
    'ciudad', ciudad,
    'miembro_desde', fecha_registro
) AS cliente_json
FROM clientes;
cliente_json
{"id": 1, "nombre": "Laura Martínez", "email": "laura@example.com", "ciudad": "Madrid", "miembro_desde": "2023-06-15"}
{"id": 2, "nombre": "Diego Fernández", "email": "diego@example.com", "ciudad": "Barcelona", "miembro_desde": "2023-09-22"}
{"id": 3, "nombre": "Sofía Castro", "email": "sofia@example.com", "ciudad": "Valencia", "miembro_desde": "2024-01-10"}

Cada fila de la tabla se convierte en un objeto JSON independiente. Las fechas se convierten automáticamente a cadenas en formato ISO 8601.

Caso práctico: respuesta de API con datos de varias tablas

En aplicaciones reales, una respuesta de API suele combinar datos de varias tablas. JSON_OBJECT junto con JSON_ARRAY y subconsultas permite construir respuestas complejas directamente en SQL:

CREATE TABLE pedidos (
    id INT AUTO_INCREMENT PRIMARY KEY,
    cliente_id INT,
    total DECIMAL(10,2),
    estado VARCHAR(20),
    fecha DATE,
    FOREIGN KEY (cliente_id) REFERENCES clientes(id)
);
 
INSERT INTO pedidos (cliente_id, total, estado, fecha) VALUES
(1, 159.98, 'entregado', '2024-02-10'),
(1, 89.99, 'enviado', '2024-03-05'),
(2, 249.00, 'entregado', '2024-01-20');
SELECT JSON_OBJECT(
    'cliente', JSON_OBJECT(
        'id', c.id,
        'nombre', c.nombre,
        'email', c.email
    ),
    'pedido', JSON_OBJECT(
        'id', p.id,
        'total', p.total,
        'estado', p.estado,
        'fecha', p.fecha
    )
) AS respuesta
FROM pedidos p
JOIN clientes c ON p.cliente_id = c.id
WHERE p.id = 2;
respuesta
{"cliente": {"id": 1, "nombre": "Laura Martínez", "email": "laura@example.com"}, "pedido": {"id": 2, "total": 89.99, "estado": "enviado", "fecha": "2024-03-05"}}

Construir la respuesta JSON directamente en SQL evita que la aplicación tenga que hacer múltiples consultas y ensamblar la respuesta manualmente.

Caso práctico: combinar con JSON_ARRAYAGG

Para construir una estructura donde un cliente tenga un array con todos sus pedidos, combinas JSON_OBJECT con JSON_ARRAYAGG:

SELECT JSON_OBJECT(
    'id', c.id,
    'nombre', c.nombre,
    'pedidos', JSON_ARRAYAGG(
        JSON_OBJECT(
            'pedido_id', p.id,
            'total', p.total,
            'estado', p.estado,
            'fecha', p.fecha
        )
    )
) AS perfil_cliente
FROM clientes c
JOIN pedidos p ON c.id = p.cliente_id
GROUP BY c.id, c.nombre;
perfil_cliente
{"id": 1, "nombre": "Laura Martínez", "pedidos": [{"pedido_id": 1, "total": 159.98, "estado": "entregado", "fecha": "2024-02-10"}, {"pedido_id": 2, "total": 89.99, "estado": "enviado", "fecha": "2024-03-05"}]}
{"id": 2, "nombre": "Diego Fernández", "pedidos": [{"pedido_id": 3, "total": 249.00, "estado": "entregado", "fecha": "2024-01-20"}]}

JSON_ARRAYAGG agrupa los objetos de pedidos en un array para cada cliente, y el JSON_OBJECT externo envuelve todo en un objeto con la información del cliente y sus pedidos.

Manejo de NULL

Cuando un valor es NULL, se incluye como null en el objeto JSON:

SELECT JSON_OBJECT(
    'nombre', 'Carlos',
    'telefono', NULL,
    'email', 'carlos@example.com'
) AS contacto;
contacto
{"nombre": "Carlos", "telefono": null, "email": "carlos@example.com"}

La clave se mantiene en el objeto con el valor null. Si quieres omitir completamente las claves con valor nulo, necesitarás construir el objeto condicionalmente, o usar JSON_REMOVE después para eliminar las claves con valor null.

Combinación con otras funciones

Si proporcionas la misma clave más de una vez, MySQL conserva solo el último valor:

SELECT JSON_OBJECT(
    'nombre', 'Primero',
    'valor', 100,
    'nombre', 'Segundo'
) AS duplicado;
duplicado
{"nombre": "Segundo", "valor": 100}

El segundo valor para la clave nombre sobrescribe al primero. Aunque MySQL no genera un error, es mejor evitar claves duplicadas para que el código sea claro y predecible.

JSON_OBJECT se integra con todas las funciones JSON de MySQL. Puedes almacenar objetos creados con JSON_OBJECT directamente en columnas JSON, pasarlos como argumentos a JSON_MERGE_PATCH para combinar objetos, o usarlos con JSON_EXTRACT para leer valores de objetos creados dinámicamente.

En el siguiente artículo veremos JSON_EXTRACT para extraer valores de documentos JSON.

Escrito por Eduardo Lázaro