Pourquoi json_encode retournerait-il une chaîne vide

108

J'ai une structure php simple avec 3 tableaux imbriqués.

Je n'utilise pas d'objets particuliers et je construis moi-même les tableaux avec 2 boucles imbriquées.

Voici un exemple du var_dump du tableau que je souhaite convertir en 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

Dans un autre script, j'ai une structure similaire et json_encodefonctionne très bien. Donc je ne comprends pas pourquoi json_encodene fonctionnera pas ici.

Edit: il semble y avoir un problème avec l'encodage. Quand mb_detect_encodingretourne ASCII, json_encodeça marche mais quand ça retourne UTF8, ça ne marche plus.

Edit2: json_last_error()renvoie JSON_ERROR_UTF8ce qui signifie: Caractères UTF-8 mal formés, éventuellement mal encodés .

Matthieu Riegler
la source
Le manuel PHP dit This function only works with UTF-8 encoded data.qu'il ne devrait y avoir aucun problème d'encodage.
MahanGM
13
Essayez d'utiliser utf8_encode()les namechamps de votre tableau avant de remettre la chaîne json_encode().
MahanGM
THX ! Je suis venu moi-même à cette solution qui a résolu mon problème.
Matthieu Riegler
Ouais, j'ai vu la réponse. Bonne chance.
MahanGM
3
Utilisez l' JSON_PARTIAL_OUTPUT_ON_ERRORoption pour voir le problème (par exemple, le champ avec UTF8 sera nul).
Peter Krauss

Réponses:

255

Eh bien après 2 heures de fouille (cf Edits)

J'ai découvert ce qui suit:

  • Dans mon cas, c'est un problème d'encodage
  • mb_detect_encoding renvoie probablement une réponse défectueuse, certaines chaînes n'étaient probablement pas UTF-8
  • l'utilisation utf8_encode()de ces chaînes a résolu mon problème, mais voir la note ci-dessous

Voici une fonction récursive qui peut forcer la conversion en UTF-8 de toutes les chaînes contenues dans un tableau:

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;
}

Utilisez-le simplement comme ceci:

echo json_encode(utf8ize($data));

Remarque: utf8_encode () encode la chaîne ISO-8859-1 en UTF-8 selon la documentation, donc si vous n'êtes pas sûr de l'encodage d'entrée iconv () ou mb_convert_encoding () peuvent être de meilleures options comme indiqué dans les commentaires et autres solutions.

Matthieu Riegler
la source
4
Merci pour votre solution ... cependant, une note latérale: changez le } else {en } else if (is_string ($d)) {; sinon vous changerez tout en chaînes (par exemple INTdeviendra a STRING).
Paul Peelen
3
Tu viens de me sauver la vie. J'étais sur le point de tout terminer jusqu'à ce que je trouve cette fonction !! Je vous remercie.
silversunhunter
2
WTF! Merci d'avoir partagé votre solution. Je peux voir que cela aurait pris beaucoup de fouilles pour comprendre, et je vous suis reconnaissant de faire cela et de partager.
kris le
1
Après trois jours de débogage, je pourrais vous embrasser tout de suite.
AJB
2
Si vous lisez depuis la base de données, utilisez simplement $ conn-> set_charset ("utf8");
Andrew Briggs
36

Matthieu Riegler a présenté une très bonne solution mais j'ai dû la modifier légèrement pour gérer les objets aussi:

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;
}

Une dernière remarque: json_last_error () peut être utile pour déboguer les fonctions json_encode () / json_encode ().

Adam Bubela
la source
Ne devrait-il pas être à la elseifplace de else if? (c'est-à-dire pas de blanc).
Uwe Keim
2
@UweKeim selon la documentation PHP "elseif et else if ne seront considérés exactement de la même manière que si vous utilisez des accolades" ce qui signifie qu'ils sont équivalents tant que vous n'utilisez pas la notation deux-points par exempleif(): elseif:
Adam Bubela
1
Bon travail. PHP est une poubelle et des gars comme vous l'empêchent d'aller à la décharge.
Lonnie Best
Vous devez insérer un else if(is_int($d)||is_bool($d)) return $d;avant le dernier, à cause de:{"success":true, "message":"Ⲃⲟⲟ𝓵ⲉⲁⲛ ⲁⲛⲇ Ⲓⲛϯⲉ𝓰ⲉꞅ𝛓"}
David Refoua
Tout comme @ paul-peelen recommandé à @ matthieu-riegler: changez le dernier elsepour else if(is_string ($d)); sinon vous changerez tout en chaînes (par exemple INTdeviendra a STRING).
Bruno Serrano
30

Pour moi, la réponse à ce problème était de configurer charset=utf8ma connexion PDO.

$dbo = new PDO('mysql:host=localhost;dbname=yourdb;charset=utf8', $username, $password);
fayd
la source
2
Ou dans les fonctions mysqli: mysqli_set_charset ($ connection, "utf8");
user18099
C'était l'indication de ma solution. Petite cause différente de la connexion msqli. Appelez simplement $mysqli->set_charset("utf8");après avoir manipulé votre base de données.
MaggusK
Veuillez utiliser utf8mb4dans les versions récentes de MySQL. utf8est obsolète.
Dharman
10

Adam Bubela a également présenté une très bonne solution qui m'a aidé à résoudre mon problème, et voici la fonction simplifiée:

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
la source
1
J'aime celui-ci car il préserve les clés.
dev0
7

J'ai exactement le même problème sur PHP 5.6. J'utilise Open Server + Nginx sur Windows 7. Tous les jeux de caractères sont définis sur UTF-8. En théorie, selon la documentation officielle , le drapeau

JSON_UNESCAPED_UNICODE

devrait résoudre ce problème. Ce n'est malheureusement pas mon cas. Je ne sais pas pourquoi. Tous les extraits ci-dessus ne résolvent pas mon problème, j'ai donc trouvé ma propre implémentation. Je pense que cela pourrait aider quelqu'un. Au moins, les lettres russes passent le test.

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
la source
4

Cette réponse acceptée fonctionne. Mais dans le cas où vous récupérez vos données de MySQL (comme je l'étais), il existe un moyen plus simple.

Une fois que vous ouvrez votre base de données, avant d'interroger, vous pouvez définir le jeu de caractères à l'aide de mysqli comme suit:

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

OU

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

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

Kholofelo
la source
4

J'ai rencontré ce problème sur un serveur exécutant une ancienne version de PHP (5.2). J'utilisais l'indicateur JSON_FORCE_OBJECT, et apparemment, cela n'est pas pris en charge avant la 5.3

Donc, si vous utilisez cet indicateur, assurez-vous de vérifier votre version!

Une solution de contournement semble être simplement la conversion en un objet avant l'encodage, comme:

json_encode((object)$myvar);
Shane N
la source
3

Le retour de mb_detect_encodingpeut ne pas être correct:

$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'))
);

En fonction de l'ordre de détection par défaut, ce qui précède peut renvoyer des résultats différents, de sorte que l'encodage est faussement signalé comme UTF-8. ( Voici un exemple plus large .)

Il est probable que vos données ne soient pas encodées au format UTF-8, elles sont donc json_encoderenvoyées false. Vous devriez regarder à convertir vos chaînes en UTF-8 avant le codage 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
la source
3

J'obtenais des données de ob_get_clean () et j'ai eu le même problème, mais les solutions ci-dessus ne fonctionnent pas pour moi. Dans mon cas, la solution était la suivante, peut-être qu'elle aidera quelqu'un.

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

l'utilisation de utf8_encode () sur ces chaînes a résolu mon problème.

mobizen
la source
1

J'ai amélioré la réponse d'Adam Bubela. Je déteste juste quand les blocs ne sont pas fermés par {et}. C'est plus propre et vous n'introduisez pas de bogues ou c'est peut-être que j'ai utilisé Perl dans le passé :)

<?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
la source
0

Si vous récupérez ces données à partir d'une base de données, utilisez mysqli_set_charset($connection, "utf8");en connexion lorsque vous récupérez les paramètres de la base de données

raul garcia de la fuente
la source
0

ce problème survient parfois - vous ne transmettez pas le contrôle d'accès à l'en-tête.

Dans mon cas, si on m'a ajouté un écho avant json_encode. Il montrait le résultat sinon une page blanche arrivait.

J'ai ajouté

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

et mon problème résolu.

Anup
la source