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;
objetoarray_valcadenaenterodecimal_valbooleanonulo
OBJECTARRAYSTRINGINTEGERDOUBLEBOOLEANNULL

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_nullsql_null
NULLNULL

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_nombretipo_preciotipo_disponible
STRINGINTEGERBOOLEAN

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;
nombretipo_preciotipo_stocktipo_tagstipo_specs
iPhone 15 ProINTEGERINTEGERARRAYOBJECT
Funda siliconaSTRINGSTRINGSTRINGNULL
Samsung Galaxy S24INTEGERINTEGERARRAYOBJECT
Cable USB-CINTEGERBOOLEANARRAYOBJECT

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';
nombreproblema
Funda siliconaprecio no es número
Funda siliconastock no es número
Cable USB-Cstock no es número
Funda siliconatags 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;
clavetipodescripcion
max_intentosINTEGERNúmero: 5
modo_debugBOOLEANBooleano: activado
servidores_permitidosARRAYLista con 2 elementos
mensaje_mantenimientoSTRINGTexto: El sistema estará en mantenimiento de 2:00 a 4:00 AM
límitesOBJECTObjeto 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;
endpointtipo_respuestainterpretacion
/usuarios/1OBJECTRegistro único
/usuarios/buscarARRAYColección (3 items)
/usuarios/countINTEGERValor escalar numérico
/usuarios/999NULLSin resultado
/estadoSTRINGMensaje 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 1

Por 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;
nombrenum_especificacionesnum_tags
iPhone 15 Pro22
Funda silicona00
Samsung Galaxy S2422
Cable USB-C00

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