¿Por qué json_encode devolvería una cadena vacía?

107

Tengo una estructura php simple con 3 matrices anidadas.

No utilizo objetos particulares y construyo las matrices con 2 bucles anidados.

Aquí hay una muestra del var_dump de la matriz que quiero convertir a Json.

array (size=2)
  'tram B' => 
    array (size=2)
      0 => 
        array (size=3)
          'name' => string 'Ile Verte' (length=9)
          'distance' => int 298
          'stationID' => int 762
      1 => 
        array (size=3)
          'name' => string 'La Tronche Hôpital' (length=18)
          'distance' => int 425
          'stationID' => int 771
  16 => 
    array (size=4)
      0 => 
        array (size=3)
          'name' => string 'Bastille' (length=8)
          'distance' => int 531
          'stationID' => int 397
      1 => 
        array (size=3)
          'name' => string 'Xavier Jouvin' (length=13)
          'distance' => int 589
          'stationID' => int 438

En otro script tengo una estructura similar y json_encodefunciona bien. Entonces no entiendo por qué json_encodeno funcionará aquí.

Editar: parece haber un problema con la codificación. Cuando mb_detect_encodingdevuelve ASCII, json_encodefunciona, pero cuando devuelve UTF8, ya no funciona.

Edit2: json_last_error()devuelve lo JSON_ERROR_UTF8que significa: caracteres UTF-8 mal formados , posiblemente codificados incorrectamente .

Matthieu Riegler
fuente
El manual de PHP dice This function only works with UTF-8 encoded data.que no debería haber ningún problema con la codificación.
MahanGM
13
Intente usar utf8_encode()en sus namecampos de matriz antes de entregar la cadena a json_encode().
MahanGM
Gracias ! Acabo de llegar yo mismo a esta solución que resolvió mi problema.
Matthieu Riegler
Sí, vi la respuesta. Buena suerte.
MahanGM
3
Use la JSON_PARTIAL_OUTPUT_ON_ERRORopción para ver el problema (por ejemplo, el campo con UTF8 será nulo).
Peter Krauss

Respuestas:

255

Bueno, después de 2 horas de excavación (cf. Ediciones)

Descubrí lo siguiente:

  • En mi caso es un problema de codificación
  • mb_detect_encoding devuelve probablemente una respuesta defectuosa, algunas cadenas probablemente no eran UTF-8
  • usar utf8_encode()en esa cadena resolvió mi problema, pero vea la nota a continuación

Aquí hay una función recursiva que puede forzar la conversión a UTF-8 de todas las cadenas contenidas en una matriz:

function utf8ize($d) {
    if (is_array($d)) {
        foreach ($d as $k => $v) {
            $d[$k] = utf8ize($v);
        }
    } else if (is_string ($d)) {
        return utf8_encode($d);
    }
    return $d;
}

Úselo simplemente así:

echo json_encode(utf8ize($data));

Nota: utf8_encode () codifica la cadena ISO-8859-1 a UTF-8 según los documentos, por lo que si no está seguro de la codificación de entrada, iconv () o mb_convert_encoding () pueden ser mejores opciones como se indica en los comentarios y otras soluciones.

Matthieu Riegler
fuente
4
Gracias por su solución ... sin embargo, una nota al margen: cambie el } else {a } else if (is_string ($d)) {; de lo contrario, cambiará todo a cadenas (p. ej.INT se convertirá en a STRING).
Paul Peelen
3
Me acaba de salvar la vida. Estaba a punto de terminar con todo hasta que encontré esta función !! Gracias.
silversunhunter
2
¡WTF! Gracias por compartir tu solución. Puedo ver que habría sido necesario investigar mucho para averiguarlo, y estoy agradecido por hacer eso y compartirlo.
kris
1
Después de tres días de depuración, podría besarte ahora mismo.
AJB
2
Si lee de la base de datos, use $ conn-> set_charset ("utf8");
Andrew Briggs
36

Matthieu Riegler presentó una solución realmente buena, sin embargo, tuve que modificarla ligeramente para manejar objetos también:

function utf8ize($d) {
    if (is_array($d)) 
        foreach ($d as $k => $v) 
            $d[$k] = utf8ize($v);

     else if(is_object($d))
        foreach ($d as $k => $v) 
            $d->$k = utf8ize($v);

     else 
        return utf8_encode($d);

    return $d;
}

Una nota más: json_last_error () puede ser útil para depurar funciones json_encode () / json_encode ().

Adam Bubela
fuente
¿No debería ser en elseiflugar de else if? (es decir, sin espacio en blanco).
Uwe Keim
2
@UweKeim de acuerdo con la documentación de PHP "elseif y else if solo se considerarán exactamente iguales cuando se usen llaves", lo que significa que son equivalentes siempre y cuando no use la notación de dos puntos, por ejemploif(): elseif:
Adam Bubela,
1
Buen trabajo. PHP es basura y tipos como tú evitan que se vaya al vertedero.
Lonnie Best
Debe insertar un else if(is_int($d)||is_bool($d)) return $d;antes del último, debido a:{"success":true, "message":"Ⲃⲟⲟ𝓵ⲉⲁⲛ ⲁⲛⲇ Ⲓⲛϯⲉ𝓰ⲉꞅ𝛓"}
David Refoua
Al igual que @ paul-peelen recomendó a @ matthieu-riegler: Cambie el último elsepor else if(is_string ($d)); de lo contrario, cambiará todo a cadenas (por ejemplo, INTse convertirá en a STRING).
Bruno Serrano
30

Para mí, la respuesta a este problema fue configurar charset=utf8mi conexión PDO.

$dbo = new PDO('mysql:host=localhost;dbname=yourdb;charset=utf8', $username, $password);
fayd
fuente
2
O en las funciones de mysqli: mysqli_set_charset ($ conexión, "utf8");
user18099
Esta fue la pista de mi solución. Poca causa diferente de la conexión msqli. Simplemente llame $mysqli->set_charset("utf8");después de manejar su base de datos.
MaggusK
Úselo utf8mb4en versiones recientes de MySQL. utf8es obsoleto.
Dharman
10

Adam Bubela también presentó una solución realmente buena que me ayudó a resolver mi problema, y ​​aquí está la función simplificada:

function utf8ize($d)
{ 
    if (is_array($d) || is_object($d))
        foreach ($d as &$v) $v = utf8ize($v);
    else
        return utf8_encode($d);

    return $d;
}
Alex
fuente
1
Me gusta este porque conserva las claves.
dev0
7

Tengo exactamente el mismo problema en PHP 5.6. Utilizo Open Server + Nginx en Windows 7. Todos los juegos de caracteres están configurados en UTF-8. En teoría, según la documentación oficial , bandera

JSON_UNESCAPED_UNICODE

debería resolver esto. Desafortunadamente este no es mi caso. No se por que. Todos los fragmentos anteriores no resuelven mi problema, por lo que he encontrado mi propia implementación. Creo que podría ayudar a alguien. Al menos, las letras rusas pasan la prueba.

function utf8ize($d) {
    if (is_array($d) || is_object($d)) {
        foreach ($d as &$v) $v = utf8ize($v);
    } else {
        $enc   = mb_detect_encoding($d);

        $value = iconv($enc, 'UTF-8', $d);
        return $value;
    }

    return $d;
}
Vsevolod Azovsky
fuente
4

Esta respuesta aceptada funciona. Pero en caso de que obtenga sus datos de MySQL (como yo), hay una manera más fácil.

Una vez que abra su base de datos, antes de realizar la consulta, puede configurar el juego de caracteres usando mysqli de la siguiente manera:

/* change character set to utf8 | Procedural*/
if (!mysqli_set_charset($link, "utf8")) {
    printf("Error loading character set utf8: %s\n", mysqli_error($link));
    exit();
}

O

/* change character set to utf8 | Object Oriented*/
if (!$mysqli->set_charset("utf8")) {
        printf("Error loading character set utf8: %s\n", $mysqli->error);
        exit();
 }

ENLACE: http://php.net/manual/en/mysqli.set-charset.php

Kholofelo
fuente
4

Me encontré con este problema en un servidor que ejecuta una versión anterior de PHP (5.2). Estaba usando la bandera JSON_FORCE_OBJECT, y aparentemente eso no es compatible hasta 5.3

Entonces, si está usando esa bandera, ¡asegúrese de verificar su versión!

Una solución alternativa parece ser simplemente enviar a un objeto antes de codificar, como:

json_encode((object)$myvar);
Shane N
fuente
3

La devolución de mb_detect_encodingpuede no ser correcta:

$data = iconv('UTF-8', 'ISO-8859-1', 'La Tronche Hôpital');
var_dump(
    mb_detect_encoding($data),
    mb_detect_encoding($data, array('ISO-8859-1', 'UTF-8'))
);

Según el orden de detección predeterminado, lo anterior puede devolver resultados diferentes, por lo que la codificación se informa falsamente como UTF-8. ( Aquí hay un ejemplo más grande ).

Es probable que sus datos no estén codificados como UTF-8, por json_encodelo que regresan false. Debería considerar convertir sus cadenas a UTF-8 antes de la codificación JSON:

$fromEncoding = 'ISO-8859-1'; // This depends on the data

array_walk_recursive($array, function (&$value, $key, $fromEncoding) {
    if (is_string($value)) {
        $value = iconv($fromEncoding, 'UTF-8', $value);
    }
}, $fromEncoding);
cmbuckley
fuente
3

Estaba obteniendo datos de ob_get_clean () y tuve el mismo problema, pero las soluciones anteriores no me funcionan. En mi caso, la solución fue esta, tal vez ayude a alguien.

$var = mb_convert_encoding($var, 'UTF-8');
zdeniiik
fuente
2

usando utf8_encode () en esas cadenas resolvió mi problema.

mobizen
fuente
1

He mejorado la respuesta de Adam Bubela. Odio cuando los bloques no están cerrados por {y}. Es más limpio y no introduces errores o tal vez es que usé Perl en el pasado :)

<?php

class App_Updater_String_Util {    
    /**
     * Usage: App_Updater_String_Util::utf8_encode( $data );
     *
     * @param mixed $d
     * @return mixed
     * @see http://stackoverflow.com/questions/19361282/why-would-json-encode-returns-an-empty-string
     */
    public static function utf8_encode($d) {
        if (is_array($d)) {
            foreach ($d as $k => $v) {
                $d[$k] = self::utf8_encode($v);
            }
        } elseif (is_object($d)) {
            foreach ($d as $k => $v) {
                $d->$k = self::utf8_encode($v);
            }
        } elseif (is_scalar($d)) {
            $d = utf8_encode($d);
        }

        return $d;
    }
}

?>
Svetoslav Marinov
fuente
0

Si obtiene estos datos de una base de datos, utilícelos mysqli_set_charset($connection, "utf8");en conexión cuando obtenga los parámetros de la base de datos

raul garcia de la fuente
fuente
0

Este problema surge a veces: no está pasando el control de acceso al encabezado.

En mi caso, si se agregó un eco antes de json_encode. Estaba mostrando el resultado, de lo contrario venía una página en blanco.

yo añadí

header('Access-Control-Allow-Origin: *'); 

y mi problema resuelto.

Anup
fuente