Perché json_encode restituisce una stringa vuota


107

Ho una semplice struttura php con 3 array annidati.

Non utilizzo oggetti particolari e costruisco io stesso gli array con 2 loop annidati.

Ecco un esempio del var_dump dell'array che voglio convertire in 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

In un altro script ho una struttura simile e json_encodefunziona bene. Quindi non capisco perché json_encodenon funzionerà qui.

Modifica: sembra esserci un problema con la codifica. Quando mb_detect_encodingrestituisce ASCII, json_encodefunziona ma quando restituisce UTF8, non funziona più.

Modifica2: json_last_error()restituisce JSON_ERROR_UTF8che significa: caratteri UTF-8 malformati , probabilmente codificati in modo errato .


Il manuale PHP dice che This function only works with UTF-8 encoded data.non dovrebbero esserci problemi con la codifica.
MahanGM

13
Provare a utilizzare utf8_encode()sui vostri namecampi matrice prima di consegnare la stringa json_encode().
MahanGM

Grazie ! Sono appena arrivato a questa soluzione che ha risolto il mio problema.
Matthieu Riegler

Sì, ho visto la risposta. In bocca al lupo.
MahanGM

3
Usa l' JSON_PARTIAL_OUTPUT_ON_ERRORopzione per vedere il problema (es. Il campo con UTF8 sarà nullo).
Peter Krauss

Risposte:


255

Bene dopo 2 ore di scavo (cfr Modifiche)

Ho scoperto quanto segue:

  • Nel mio caso è un problema di codifica
  • mb_detect_encoding restituisce probabilmente una risposta errata, alcune stringhe probabilmente non erano UTF-8
  • l'uso utf8_encode()su quelle stringhe ha risolto il mio problema, ma vedi la nota sotto

Ecco una funzione ricorsiva che può forzare la conversione in UTF-8 di tutte le stringhe contenute in un array:

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

Usalo semplicemente in questo modo:

echo json_encode(utf8ize($data));

Nota: utf8_encode () codifica la stringa ISO-8859-1 in UTF-8 come da documentazione, quindi se non sei sicuro della codifica di input iconv () o mb_convert_encoding () potrebbero essere opzioni migliori come indicato nei commenti e in altre soluzioni.


4
Grazie per la soluzione ... tuttavia, una nota a margine: cambia } else {in } else if (is_string ($d)) {; altrimenti cambierai tutto in stringhe (ad esempio INTdiventerà a STRING).
Paul Peelen

3
Mi hai appena salvato la vita. Stavo per terminare tutto finché non ho trovato questa funzione !! Grazie.
silversunhunter

2
WTF! Grazie per aver condiviso la tua soluzione. Vedo che ci sarebbe voluto un sacco di ricerche per capirlo, e ti sono grato per averlo fatto e condiviso.
kris

1
Dopo tre giorni di debug, potrei baciarti subito.
AJB

2
Se leggi dal database usa, $ conn-> set_charset ("utf8");
Andrew Briggs

36

Matthieu Riegler ha presentato una soluzione davvero buona, tuttavia ho dovuto modificarla leggermente per gestire anche gli oggetti:

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

Un'altra nota: json_last_error () può essere utile nel debug delle funzioni json_encode () / json_encode ().


Non dovrebbe essere elseifinvece di else if? (cioè nessuno spazio vuoto).
Uwe Keim

2
@UweKeim secondo la documentazione PHP "elseif e else if saranno considerati esattamente gli stessi quando si usano le parentesi graffe" il che significa che sono equivalenti fintanto che non si utilizza la notazione dei due punti ad esempioif(): elseif:
Adam Bubela

1
Buon lavoro. PHP è spazzatura e ragazzi come te gli impediscono di finire in discarica.
Lonnie Best

Dovresti inserire una else if(is_int($d)||is_bool($d)) return $d;prima dell'ultima, a causa di:{"success":true, "message":"Ⲃⲟⲟ𝓵ⲉⲁⲛ ⲁⲛⲇ Ⲓⲛϯⲉ𝓰ⲉꞅ𝛓"}
David Refoua

Proprio come @ paul-peelen consigliato a @ matthieu-riegler: cambia l'ultimo elseper else if(is_string ($d)); altrimenti cambierai tutto in stringhe (ad esempio INTdiventerà a STRING).
Bruno Serrano

30

Per me, la risposta a questo problema è stata l'impostazione charset=utf8nella mia connessione PDO.

$dbo = new PDO('mysql:host=localhost;dbname=yourdb;charset=utf8', $username, $password);

2
O nelle funzioni mysqli: mysqli_set_charset ($ connection, "utf8");
user18099

Questo era il suggerimento per la mia soluzione. Causa leggermente diversa della connessione msqli. Basta chiamare $mysqli->set_charset("utf8");dopo aver eseguito la gestione del database.
MaggusK

Si prega di utilizzare utf8mb4nelle versioni recenti di MySQL. utf8è obsoleto.
Dharman

10

Adam Bubela ha anche presentato una soluzione davvero buona che mi ha aiutato a risolvere il mio problema, ed ecco la funzione semplificata:

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

    return $d;
}

1
Mi piace perché conserva le chiavi.
dev0

7

Ho esattamente lo stesso problema su PHP 5.6. Uso Open Server + Nginx su Windows 7. Tutti i set di caratteri sono impostati su UTF-8. In teoria, secondo la documentazione ufficiale , flag

JSON_UNESCAPED_UNICODE

dovrebbe risolvere questo problema. Purtroppo questo non è il mio caso. Non so perché. Tutti gli snippet di cui sopra non risolvono il mio problema, quindi ho trovato la mia implementazione. Credo che potrebbe aiutare qualcuno. Almeno, le lettere russe superano il 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;
}

4

Questa risposta accettata funziona. Ma nel caso tu stia ottenendo i tuoi dati da MySQL (come me) c'è un modo più semplice.

Una volta aperto il database, prima di eseguire una query è possibile impostare il set di caratteri utilizzando mysqli come segue:

/* 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();
 }

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


4

Ho riscontrato questo problema su un server che esegue una versione precedente di PHP (5.2). Stavo usando il flag JSON_FORCE_OBJECT e apparentemente non è supportato fino alla 5.3

Quindi se stai usando quel flag assicurati di controllare la tua versione!

Una soluzione alternativa sembra essere solo il casting su un oggetto prima della codifica, come:

json_encode((object)$myvar);

3

La restituzione di mb_detect_encodingpotrebbe non essere corretta:

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

A seconda dell'ordine di rilevamento predefinito, quanto sopra può restituire risultati diversi, quindi la codifica viene erroneamente segnalata come UTF-8. ( Ecco un esempio più ampio .)

È probabile che i tuoi dati non siano codificati come UTF-8, quindi json_encodestanno tornando false. Dovresti esaminare la conversione delle stringhe in UTF-8 prima della codifica 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);

3

Stavo ottenendo dati da ob_get_clean () e ho avuto lo stesso problema, ma le soluzioni precedenti non funzionano per me. Nel mio caso la soluzione era questa, forse avrebbe aiutato qualcuno.

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

2

l'utilizzo di utf8_encode () su quelle stringhe ha risolto il mio problema.


1

Ho migliorato la risposta di Adam Bubela. Odio solo quando i blocchi non vengono chiusi da {e}. È più pulito e non introduci bug o forse è che ho usato Perl in passato :)

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

?>

0

Se ottieni questi dati da un database, usali mysqli_set_charset($connection, "utf8");in connessione quando ottieni i parametri dal database


0

questo problema si verifica a volte: non stai passando il controllo dell'accesso all'intestazione.

Nel mio caso, se mi è stato aggiunto un echo prima di json_encode. Mostrava il risultato altrimenti stava arrivando una pagina vuota.

Ho aggiunto

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

e il mio problema è stato risolto.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.