JSON_TYPE
La función JSON_TYPE examina un valor JSON y devuelve una cadena que indica su tipo. En el mundo JSON existen varios tipos de datos (objetos, arrays, cadenas, números, booleanos y null), y MySQL necesita distinguirlos para procesarlos correctamente. Con JSON_TYPE puedes inspeccionar el tipo de cualquier valor JSON, ya sea un documento completo, un elemento extraído con JSON_EXTRACT, o un literal JSON. Esto resulta especialmente útil para validar datos, escribir lógica condicional, y depurar documentos JSON con estructura variable.
Sintaxis
JSON_TYPE(valor_json)La función recibe un único argumento que debe ser un valor JSON válido. Devuelve una cadena con el nombre del tipo en mayúsculas. Si el argumento no es un valor JSON válido, MySQL lanza un error. Si es NULL, devuelve NULL.
Los tipos que puede devolver JSON_TYPE son:
SELECT JSON_TYPE('{"clave": "valor"}') AS tipo_objeto;
SELECT JSON_TYPE('[1, 2, 3]') AS tipo_array;
SELECT JSON_TYPE('"texto"') AS tipo_cadena;
SELECT JSON_TYPE('42') AS tipo_entero;
SELECT JSON_TYPE('3.14') AS tipo_decimal;
SELECT JSON_TYPE('true') AS tipo_booleano;
SELECT JSON_TYPE('null') AS tipo_null;Los resultados respectivos son: OBJECT, ARRAY, STRING, INTEGER, DOUBLE, BOOLEAN y NULL. Estos son los siete tipos fundamentales del sistema de tipos JSON en MySQL.
Comportamiento básico
Veamos todos los tipos en una sola consulta para tener una referencia clara:
SELECT
JSON_TYPE('{"a": 1}') AS objeto,
JSON_TYPE('[1, 2]') AS array_val,
JSON_TYPE('"hola"') AS cadena,
JSON_TYPE('42') AS entero,
JSON_TYPE('3.14') AS decimal_val,
JSON_TYPE('true') AS booleano,
JSON_TYPE('null') AS nulo;| objeto | array_val | cadena | entero | decimal_val | booleano | nulo |
|---|---|---|---|---|---|---|
| OBJECT | ARRAY | STRING | INTEGER | DOUBLE | BOOLEAN | NULL |
Un detalle importante es la diferencia entre JSON_TYPE('null') y JSON_TYPE(NULL). El primero es un valor JSON null (el literal null dentro de JSON) y devuelve la cadena "NULL". El segundo es un valor SQL NULL y la función devuelve el valor SQL NULL:
SELECT
JSON_TYPE('null') AS json_null,
JSON_TYPE(NULL) AS sql_null;| json_null | sql_null |
|---|---|
| NULL | NULL |
Aunque el resultado se muestra igual en la tabla, son conceptos distintos. JSON_TYPE('null') devuelve la cadena "NULL" como tipo, mientras que JSON_TYPE(NULL) devuelve el valor ausente de SQL.
Cuando usas JSON_TYPE con JSON_EXTRACT, puedes inspeccionar el tipo de cualquier valor dentro de un documento:
SELECT
JSON_TYPE(JSON_EXTRACT('{"nombre": "iPhone", "precio": 1299, "disponible": true}', '$.nombre')) AS tipo_nombre,
JSON_TYPE(JSON_EXTRACT('{"nombre": "iPhone", "precio": 1299, "disponible": true}', '$.precio')) AS tipo_precio,
JSON_TYPE(JSON_EXTRACT('{"nombre": "iPhone", "precio": 1299, "disponible": true}', '$.disponible')) AS tipo_disponible;| tipo_nombre | tipo_precio | tipo_disponible |
|---|---|---|
| STRING | INTEGER | BOOLEAN |
Caso práctico: validación de estructura de productos
Imagina una tabla de productos donde el campo JSON puede contener datos con tipos inesperados debido a importaciones de distintas fuentes:
CREATE TABLE productos (
id INT PRIMARY KEY AUTO_INCREMENT,
nombre VARCHAR(100),
atributos JSON
);
INSERT INTO productos (nombre, atributos) VALUES
('iPhone 15 Pro', '{"precio": 1299, "stock": 45, "tags": ["premium", "5g"], "specs": {"ram": 8, "pantalla": 6.1}}'),
('Funda silicona', '{"precio": "9.99", "stock": "200", "tags": "accesorios", "specs": null}'),
('Samsung Galaxy S24', '{"precio": 899, "stock": 62, "tags": ["android", "5g"], "specs": {"ram": 12, "pantalla": 6.2}}'),
('Cable USB-C', '{"precio": 12, "stock": true, "tags": [], "specs": {}}');Observa que la funda tiene el precio y stock como cadenas en lugar de números, sus tags son una cadena en vez de un array, y el cable tiene true como stock. Con JSON_TYPE puedes detectar estas inconsistencias:
SELECT
nombre,
JSON_TYPE(atributos->'$.precio') AS tipo_precio,
JSON_TYPE(atributos->'$.stock') AS tipo_stock,
JSON_TYPE(atributos->'$.tags') AS tipo_tags,
JSON_TYPE(atributos->'$.specs') AS tipo_specs
FROM productos;| nombre | tipo_precio | tipo_stock | tipo_tags | tipo_specs |
|---|---|---|---|---|
| iPhone 15 Pro | INTEGER | INTEGER | ARRAY | OBJECT |
| Funda silicona | STRING | STRING | STRING | NULL |
| Samsung Galaxy S24 | INTEGER | INTEGER | ARRAY | OBJECT |
| Cable USB-C | INTEGER | BOOLEAN | ARRAY | OBJECT |
Ahora puedes filtrar los productos con datos mal tipados:
SELECT nombre, 'precio no es número' AS problema
FROM productos
WHERE JSON_TYPE(atributos->'$.precio') NOT IN ('INTEGER', 'DOUBLE')
UNION ALL
SELECT nombre, 'stock no es número' AS problema
FROM productos
WHERE JSON_TYPE(atributos->'$.stock') NOT IN ('INTEGER', 'DOUBLE')
UNION ALL
SELECT nombre, 'tags no es array' AS problema
FROM productos
WHERE JSON_TYPE(atributos->'$.tags') != 'ARRAY';| nombre | problema |
|---|---|
| Funda silicona | precio no es número |
| Funda silicona | stock no es número |
| Cable USB-C | stock no es número |
| Funda silicona | tags no es array |
Este tipo de consulta es invaluable cuando recibes datos de fuentes externas y necesitas verificar la integridad antes de procesarlos.
Caso práctico: lógica condicional basada en tipo
En aplicaciones que almacenan configuraciones flexibles, un mismo campo puede contener tipos diferentes según el contexto. Puedes usar JSON_TYPE para manejar cada caso:
CREATE TABLE configuraciones (
id INT PRIMARY KEY AUTO_INCREMENT,
clave VARCHAR(100),
valor JSON
);
INSERT INTO configuraciones (clave, valor) VALUES
('max_intentos', '5'),
('modo_debug', 'true'),
('servidores_permitidos', '["srv1.ejemplo.com", "srv2.ejemplo.com"]'),
('mensaje_mantenimiento', '"El sistema estará en mantenimiento de 2:00 a 4:00 AM"'),
('límites', '{"cpu": 80, "memoria": 90, "disco": 95}');SELECT
clave,
JSON_TYPE(valor) AS tipo,
CASE JSON_TYPE(valor)
WHEN 'INTEGER' THEN CONCAT('Número: ', valor)
WHEN 'BOOLEAN' THEN CONCAT('Booleano: ', IF(JSON_EXTRACT(valor, '$') = true, 'activado', 'desactivado'))
WHEN 'STRING' THEN CONCAT('Texto: ', JSON_UNQUOTE(valor))
WHEN 'ARRAY' THEN CONCAT('Lista con ', JSON_LENGTH(valor), ' elementos')
WHEN 'OBJECT' THEN CONCAT('Objeto con ', JSON_LENGTH(valor), ' claves')
ELSE 'Otro tipo'
END AS descripcion
FROM configuraciones;| clave | tipo | descripcion |
|---|---|---|
| max_intentos | INTEGER | Número: 5 |
| modo_debug | BOOLEAN | Booleano: activado |
| servidores_permitidos | ARRAY | Lista con 2 elementos |
| mensaje_mantenimiento | STRING | Texto: El sistema estará en mantenimiento de 2:00 a 4:00 AM |
| límites | OBJECT | Objeto con 3 claves |
Caso práctico: análisis de respuestas de API
Cuando almacenas respuestas de APIs externas, el campo de resultado puede venir en diferentes formatos. JSON_TYPE te ayuda a clasificarlos:
CREATE TABLE log_api (
id INT PRIMARY KEY AUTO_INCREMENT,
endpoint VARCHAR(200),
respuesta JSON
);
INSERT INTO log_api (endpoint, respuesta) VALUES
('/usuarios/1', '{"id": 1, "nombre": "María", "activo": true}'),
('/usuarios/buscar', '[{"id": 1}, {"id": 2}, {"id": 3}]'),
('/usuarios/count', '42'),
('/usuarios/999', 'null'),
('/estado', '"OK"');SELECT
endpoint,
JSON_TYPE(respuesta) AS tipo_respuesta,
CASE JSON_TYPE(respuesta)
WHEN 'OBJECT' THEN 'Registro único'
WHEN 'ARRAY' THEN CONCAT('Colección (', JSON_LENGTH(respuesta), ' items)')
WHEN 'INTEGER' THEN 'Valor escalar numérico'
WHEN 'NULL' THEN 'Sin resultado'
WHEN 'STRING' THEN 'Mensaje de texto'
ELSE 'Formato desconocido'
END AS interpretacion
FROM log_api;| endpoint | tipo_respuesta | interpretacion |
|---|---|---|
| /usuarios/1 | OBJECT | Registro único |
| /usuarios/buscar | ARRAY | Colección (3 items) |
| /usuarios/count | INTEGER | Valor escalar numérico |
| /usuarios/999 | NULL | Sin resultado |
| /estado | STRING | Mensaje de texto |
Manejo de NULL
Cuando el argumento de JSON_TYPE es el valor SQL NULL, la función devuelve NULL:
SELECT JSON_TYPE(NULL) AS resultado;| resultado |
|---|
| NULL |
Si intentas pasar una cadena que no es JSON válido, MySQL lanza un error:
-- Esto produce un error
SELECT JSON_TYPE('esto no es json');
-- ERROR 3141 (22032): Invalid JSON text in argument 1Por eso es buena práctica validar primero con JSON_VALID si no estás seguro de que el dato sea JSON válido, especialmente cuando trabajas con datos de entrada de usuarios.
Combinación con otras funciones
JSON_TYPE se combina naturalmente con JSON_EXTRACT para inspeccionar valores internos de un documento. Un patrón común es usarlo como guarda antes de operaciones que dependen del tipo:
SELECT
nombre,
CASE
WHEN JSON_TYPE(atributos->'$.specs') = 'OBJECT'
THEN JSON_LENGTH(atributos->'$.specs')
ELSE 0
END AS num_especificaciones,
CASE
WHEN JSON_TYPE(atributos->'$.tags') = 'ARRAY'
THEN JSON_LENGTH(atributos->'$.tags')
ELSE 0
END AS num_tags
FROM productos;| nombre | num_especificaciones | num_tags |
|---|---|---|
| iPhone 15 Pro | 2 | 2 |
| Funda silicona | 0 | 0 |
| Samsung Galaxy S24 | 2 | 2 |
| Cable USB-C | 0 | 0 |
Esta consulta evita errores al verificar que specs sea un objeto y tags sea un array antes de contar sus elementos. Sin esta comprobación, JSON_LENGTH sobre un valor null JSON devolvería NULL, lo que podría propagar valores inesperados en el resultado. En el siguiente artículo veremos JSON_LENGTH para contar elementos.
Escrito por Eduardo Lázaro
