php

Codificar y decodificar JSON en PHP: todo lo que necesitas saber

PHP es un lenguaje del lado del servidor en donde se puede crear una API que se comunique a través de JSON, tanto para leer peticiones como para responder. Hoy veremos cómo codificar y decodificar JSON en PHP con funciones nativas.

Por defecto, PHP tiene algunos errores al codificar en JSON, ya que a veces algunos números no son codificados como números, sino como cadenas; por eso veremos cómo evitar eso y personalizar la salida gracias a las constantes JSON de PHP.

Trabajando con JSON en PHP

También veremos cómo manejar los errores; limitar la profundidad de los objetos y otras cosas muy importantes al trabajar con JSON en el lenguaje PHP.

Con PHP y JSON podemos codificar cualquier tipo de variable; no importa si es un booleano, entero, cadena o flotante; todo se codifica correctamente. Lo mismo es para los arreglos u objetos; u objetos con arreglos en su interior.

Requisitos y/o recomendaciones

Si deseas probar los ejemplos, primero instala XAMPP. Después puedes ejecutar los archivos desde la terminal, o probarlos en línea.

¿Quieres codificar y decodificar JSON en JavaScript? mira este post.

Convertir valores de PHP a su representación JSON

Para convertir una variable de PHP a su representación en JSON se usa la función json_encode. Esta función regresa una cadena (o false en caso de error), y recibe 3 argumentos; los últimos dos opcionales.

El primer argumento es la variable que va a codificar. El segundo argumento es una mascara de bits con algunas constantes predefinidas que veremos más abajo. El tercer argumento es la profundidad; si tenemos un objeto con más objetos en su interior, podemos especificar que no se codifique todo, sino hasta un cierto nivel de profundidad.

Aquí hay algunos ejemplos de cómo se codifica JSON en PHP. Más abajo veremos al segundo y tercer argumento en acción.

<?php
$nombre = "Luis";
echo json_encode($nombre) . "\n";
// "Luis"
$edad = 10;
echo json_encode($edad) . "\n";
// 10

$arreglo = [1, 2, 3];
echo json_encode($arreglo) . "\n";
// [1,2,3]

$otroArreglo = ["Hola", "Mundo", "Soy", "Un", "Arreglo"];
echo json_encode($otroArreglo) . "\n";
// ["Hola","Mundo","Soy","Un","Arreglo"]

$objeto = new StdClass;
$objeto->unaPropiedad = 1;
$objeto->otraPropiedad = "Hola";
$objeto->unArreglo = [1, 2, 3];
echo json_encode($objeto) . "\n";
// {"unaPropiedad":1,"otraPropiedad":"Hola","unArreglo":[1,2,3]} 

$otroObjeto = [
 "nombre" => "Luis",
 "sitio" => "parzibyte.me",
 "edad" => 2,
];
echo json_encode($otroObjeto) . "\n";
// {"nombre":"Luis","sitio":"parzibyte.me","edad":2}

Ahora sí veamos más ejemplos.

El segundo argumento de json_encode: constantes para modificar el comportamiento

Múltiples constantes se pueden usar en combinación, usando el operador |. La lista completa de las constantes que podemos usar está aquí.

Lo que mostraré será cómo forzar que la salida sea numérica y no como cadena.

Para ello se utiliza la constante JSON_NUMERIC_CHECK al llamara a json_encode. Aquí un ejemplo:

<?php
$numero = 123.20;
// A veces esto sale como cadena "123.20":
echo json_encode($numero);
# Por eso lo forzamos a que sea número
echo json_encode($numero, JSON_NUMERIC_CHECK);

Hay otras constantes útiles, por ejemplo, JSON_PRETTY_PRINT agrega espacios para que el JSON se muestre con las tabulaciones y espacios que lo hacen legible; útil si estamos imprimiendo directamente el JSON generado.

Otra constante útil es JSON_PRESERVE_ZERO_FRACTION, que mantiene el .0 en un número flotante y obliga a que se especifique que dicho número es flotante; por ejemplo, si tenemos el número 15.0 y lo codificamos, se convierte en 15; pero si se pone la constante, ahora sí se imprime bien. Veamos el ejemplo:

<?php
$flotante = 15.0;
echo json_encode($flotante) . "\n";
// 15

# Forzamos a que mantenga el .0
echo json_encode($flotante, JSON_PRESERVE_ZERO_FRACTION) . "\n";
// 15.0

De ahí las demás constantes pueden servirnos dependiendo de nuestras necesidades. Por cierto, podemos combinarlas usando |. Si quiero que se imprima de forma legible (JSON_PRETTY_PRINT) y que mantenga el cero en los flotantes (JSON_PRESERVE_ZERO_FRACTION), hago esto:

<?php
// Un objeto con flotantes para ver las tabulaciones y obligar a mantener el cero
$objeto = [
 "idCuenta" => "123ASD",
 "cliente" => "parzibyte",
 "saldo" => 250.0,
 "movimientos" => [
  [
   "tipo" => "salida",
   "monto" => 125.0,
   "fecha" => "2018-12-25"
  ],
  [
   "tipo" => "salida",
   "monto" => 666.55,
   "fecha" => "2018-12-26"
  ]
 ]
];
echo json_encode($objeto, JSON_PRETTY_PRINT | JSON_PRESERVE_ZERO_FRACTION);
/*
{
    "idCuenta": "123ASD",
    "cliente": "parzibyte",
    "saldo": 250.0,
    "movimientos": [
        {
            "tipo": "salida",
            "monto": 125.0,
            "fecha": "2018-12-25"
        },
        {
            "tipo": "salida",
            "monto": 666.55,
            "fecha": "2018-12-26"
        }
    ]
} 
*/

Así podemos combinar todas las constantes predefinidas; eso fue un ejemplo pero podemos combinar cualquiera.

El tercer argumento de json_encode: la profundidad máxima

Si especificamos la profundidad máxima, al codificar un objeto JSON en PHP se regresará false si se excede la misma. Aquí un ejemplo:

<?php
$objeto = [
 // Nivel 1
 "idCuenta" => "123ASD",
 "cliente" => "parzibyte",
 "saldo" => 250.0,
 "movimientos" => [
  // Nivel 2
  [
   "tipo" => "salida",
   "monto" => 125.0,
   "fecha" => "2018-12-25",
   "involucrados" => [
    // Nivel 3
    "nombre" => "Un nombre",
    "idCuenta" => "456465"
   ]
  ],
  [
   "tipo" => "salida",
   "monto" => 666.55,
   "fecha" => "2018-12-26"
  ]
 ]
];
// Con profundidad 4 todo va bien
echo "Con profundidad 4:" . json_encode($objeto, 0, 4) . "\n";
# Con profundidad 4:{"idCuenta":"123ASD","cliente":"parzibyte","saldo":250,"movimientos":[{"tipo":"salida","monto":125,"fecha":"2018-12-25","involucrados":{"nombre":"Un nombre","idCuenta":"456465"}},{"tipo":"salida","monto":666.55,"fecha":"2018-12-26"}]}


// Pero si lo ponemos a 3, json_encode devuelve FALSE y no se imprime nada
echo "Con profundidad de 3:" . json_encode($objeto, 0, 3) . "\n";
# 

Estos límites sirven para cuando no sabemos qué tan profundo es un objeto y queremos protegernos de alguna manera. Entre más niveles de profundidad, más tiempo se tarda en codificar; por eso es que existe esta opción.

Si queremos manejar el error, podemos usar una función que está más abajo.

Manejar errores al codificar JSON en PHP

Si de alguna manera json_encode devuelve false, debemos ver qué salió mal. Para ello, PHP proporciona igualmente algunas constantes de error. Me he dado a la tarea de crear una función que nos permite saber qué cosa está mal.

Recuerda que los errores no deben imprimirse, en cambio deberían guardarse en un log.

<?php
// La función que nos dice el error
function obtenerErrorDeJSON()
{
    switch (json_last_error()) {
        case JSON_ERROR_NONE:
            return "No ha ocurrido ningún error";
        case JSON_ERROR_DEPTH:
            return "Se ha excedido la profundidad máxima de la pila.";
        case JSON_ERROR_STATE_MISMATCH:
            return "Error por desbordamiento de buffer o los modos no coinciden";
        case JSON_ERROR_CTRL_CHAR:
            return "Error del carácter de control, posiblemente se ha codificado de forma incorrecta.";
        case JSON_ERROR_SYNTAX:
            return "Error de sintaxis.";
        case JSON_ERROR_UTF8:
            return "Caracteres UTF-8 mal formados, posiblemente codificados incorrectamente.";
        case JSON_ERROR_RECURSION:
            return "El objeto o array pasado a json_encode() incluye referencias recursivas y no se puede codificar.";
        case JSON_ERROR_INF_OR_NAN:
            return "El valor pasado a json_encode() incluye NAN (Not A Number) o INF (infinito)";
        case JSON_ERROR_UNSUPPORTED_TYPE:
            return "Se proporcionó un valor de un tipo no admitido para json_encode(), tal como un resource.";
        default:
            return "Error desconocido";
    }
}

# Hora de probarla. Tomamos el ejemplo anterior...
$objeto = [
    // Nivel 1
    "idCuenta" => "123ASD",
    "cliente" => "parzibyte",
    "saldo" => 250.0,
    "movimientos" => [
        // Nivel 2
        [
            "tipo" => "salida",
            "monto" => 125.0,
            "fecha" => "2018-12-25",
            "involucrados" => [
                // Nivel 3
                "nombre" => "Un nombre",
                "idCuenta" => "456465",
            ],
        ],
        [
            "tipo" => "salida",
            "monto" => 666.55,
            "fecha" => "2018-12-26",
        ],
    ],
];

// Con profundidad de 3 para que se genere un error
$codificado = json_encode($objeto, 0, 3);
if ($codificado === false) {
    echo "Ocurrió un error al codificar: " . obtenerErrorDeJSON();
} else {
    echo $codificado;
}
// Salida: Ocurrió un error al codificar: Se ha excedido la profundidad máxima de la pila.

Y así es como terminamos la parte de codificar JSON en PHP.

Decodificar JSON

Para convertir una cadena JSON a su representación como variable dentro del lenguaje, usamos json_decode. Esta función también tiene sus argumentos:

  1. El primer argumento es la cadena que vamos a decodificar
  2. Como segundo argumento, se especifica si queremos que los objetos se codifiquen como arreglos asociativos. Por defecto, se devuelve como objeto y podemos acceder a sus propiedades con $objeto->clave. En caso de que sea un arreglo asociativo, se accede con $objeto["clave"]
  3. El tercer argumento indica la profundidad; la misma que veíamos anteriormente al codificar, pero ahora para decodificar. Por defecto el valor es de 512.
  4. Como último argumento tenemos a las opciones, que igualmente son constantes. Al momento de escribir el post sólo existe JSON_BIGINT_AS_STRING que convierte valores numéricos grandes a flotantes.

Devuelve el valor NULL en caso de error, o el valor codificado en caso de éxito.

Aquí tenemos un ejemplo:

<?php
# Esto puede venir de cualquier lugar, aquí lo ponemos en una variable directamente
$codificado = '{"idCuenta":"123ASD","cliente":"parzibyte","saldo":250,"movimientos":[{"tipo":"salida","monto":125,"fecha":"2018-12-25","involucrados":{"nombre":"Un nombre","idCuenta":"456465"}},{"tipo":"salida","monto":666.55,"fecha":"2018-12-26"}]}';

$decodificado = json_decode($codificado);

// Comprobamos si no devolvió NULL:
if($decodificado === NULL){
 echo "Error decodificando";
}else{
 // Podemos acceder a las propiedades
 echo "El cliente es: " . $decodificado->cliente;
}

Es fácil decodificar, llamamos a la función y devuelve el JSON decodificado. En ese caso fue un objeto pero se puede decodificar cualquier variable, por ejemplo un arreglo, una cadena, un booleano, etcétera.

Como arreglo asociativo

Ahora veamos el segundo argumento. Si lo ponemos en true, devuelve un arreglo asociativo.

<?php
# Esto puede venir de cualquier lugar, aquí lo ponemos en una variable directamente
$codificado = '{"idCuenta":"123ASD","cliente":"parzibyte","saldo":250,"movimientos":[{"tipo":"salida","monto":125,"fecha":"2018-12-25","involucrados":{"nombre":"Un nombre","idCuenta":"456465"}},{"tipo":"salida","monto":666.55,"fecha":"2018-12-26"}]}';

$comoObjeto = json_decode($codificado);
echo "El saldo es: " . $comoObjeto->saldo . "\n";

$comoArreglo = json_decode($codificado, true);
echo "El saldo es: " . $comoArreglo["saldo"];

La diferencia es que los objetos se convierten a arreglos.

Especificar profundidad

El tercer argumento es la profundidad, la cual es 512 por defecto. Podemos cambiarla ya sea para que sea mayor o para que sea menor. En caso de que el objeto exceda la profundidad, se devuelve NULL.

<?php
// La función que nos dice el error
function obtenerErrorDeJSON()
{
    switch (json_last_error()) {
        case JSON_ERROR_NONE:
            return "No ha ocurrido ningún error";
        case JSON_ERROR_DEPTH:
            return "Se ha excedido la profundidad máxima de la pila.";
        case JSON_ERROR_STATE_MISMATCH:
            return "Error por desbordamiento de buffer o los modos no coinciden";
        case JSON_ERROR_CTRL_CHAR:
            return "Error del carácter de control, posiblemente se ha codificado de forma incorrecta.";
        case JSON_ERROR_SYNTAX:
            return "Error de sintaxis.";
        case JSON_ERROR_UTF8:
            return "Caracteres UTF-8 mal formados, posiblemente codificados incorrectamente.";
        case JSON_ERROR_RECURSION:
            return "El objeto o array pasado a json_encode() incluye referencias recursivas y no se puede codificar.";
        case JSON_ERROR_INF_OR_NAN:
            return "El valor pasado a json_encode() incluye NAN (Not A Number) o INF (infinito)";
        case JSON_ERROR_UNSUPPORTED_TYPE:
            return "Se proporcionó un valor de un tipo no admitido para json_encode(), tal como un resource.";
        default:
            return "Error desconocido";
    }
}


# Esto puede venir de cualquier lugar, aquí lo ponemos en una variable directamente
$codificado = '{"idCuenta":"123ASD","cliente":"parzibyte","saldo":250,"movimientos":[{"tipo":"salida","monto":125,"fecha":"2018-12-25","involucrados":{"nombre":"Un nombre","idCuenta":"456465"}},{"tipo":"salida","monto":666.55,"fecha":"2018-12-26"}]}';

$decodificado = json_decode($codificado, false, 1);
if($decodificado === null){
 echo "Error decodificando: " . obtenerErrorDeJSON();
}else{
 echo $decodificado->cliente;
}

Se reutiliza la función para comprobar errores. El error es que se excedió la profundidad. Estos límites igualmente funcionan cuando recibimos un objeto y queremos limitar su profundidad por seguridad o porque queremos poner el límite.

Un uso sería al crear una API en donde los usuarios manden un JSON para hacer determinada cosa. Podríamos limitar la profundidad para evitar que intenten agotar los recursos del servidor.

Referencias

Me he basado en lo que hay en el sitio oficial de PHP aquí y aquí.

 

Estoy aquí para ayudarte 🤝💻


Estoy aquí para ayudarte en todo lo que necesites. Si requieres alguna modificación en lo presentado en este post, deseas asistencia con tu tarea, proyecto o precisas desarrollar un software a medida, no dudes en contactarme. Estoy comprometido a brindarte el apoyo necesario para que logres tus objetivos. Mi correo es parzibyte(arroba)gmail.com, estoy como@parzibyte en Telegram o en mi página de contacto

No te pierdas ninguno de mis posts 🚀🔔

Suscríbete a mi canal de Telegram para recibir una notificación cuando escriba un nuevo tutorial de programación.
parzibyte

Programador freelancer listo para trabajar contigo. Aplicaciones web, móviles y de escritorio. PHP, Java, Go, Python, JavaScript, Kotlin y más :) https://parzibyte.me/blog/software-creado-por-parzibyte/

Ver comentarios

  • Hola queria saber si hay alguna forma de retorna imagens del banco de dato utilizando json_encode.
    Quiero hacer un sistema de scroll inifinito en mi pagina pero necesito retorna imagens

    • Hola, podrías mostrar la lista de imágenes y luego agregarlas al HTML ya sea con JavaScript puro o con un framework.
      Saludos :)

Entradas recientes

Creador de credenciales web – Aplicación gratuita

Hoy te voy a presentar un creador de credenciales que acabo de programar y que…

2 semanas hace

Desplegar PWA creada con Vue 3, Vite y SQLite3 en Apache

Ya te enseñé cómo convertir una aplicación web de Vue 3 en una PWA. Al…

2 semanas hace

Arquitectura para wasm con Go, Vue 3, Pinia y Vite

En este artículo voy a documentar la arquitectura que yo utilizo al trabajar con WebAssembly…

2 semanas hace

Vue 3 y Vite: crear PWA (Progressive Web App)

En un artículo anterior te enseñé a crear un PWA. Al final, cualquier aplicación que…

2 semanas hace

Errores de Comlink y algunas soluciones

Al usar Comlink para trabajar con los workers usando JavaScript me han aparecido algunos errores…

2 semanas hace

Esperar promesa para inicializar Store de Pinia con Vue 3

En este artículo te voy a enseñar cómo usar un "top level await" esperando a…

2 semanas hace

Esta web usa cookies.