Por que json_encode retornaria uma string vazia

107

Eu tenho uma estrutura simples de php com 3 matrizes aninhadas.

Eu não uso objetos específicos e construo as matrizes com 2 loops aninhados.

Aqui está um exemplo do var_dump da matriz que desejo converter para 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

Em outro script eu tenho uma estrutura semelhante e json_encodefunciona bem. Então eu não entendo porque json_encodenão funciona aqui.

Editar: parece haver um problema com a codificação. Quando mb_detect_encodingretorna ASCII, json_encodefunciona, mas quando retorna UTF8, não funciona mais.

Edit2: json_last_error()retorna o JSON_ERROR_UTF8que significa: Caracteres UTF-8 malformados, possivelmente codificados incorretamente .

Matthieu Riegler
fonte
O manual do PHP diz This function only works with UTF-8 encoded data.que não deve haver nenhum problema com a codificação.
MahanGM
13
Tente usar utf8_encode()em seus namecampos de array antes de entregar a string json_encode().
MahanGM
THX ! Eu mesmo cheguei a uma solução que resolveu meu problema.
Matthieu Riegler
Sim, vi a resposta. Boa sorte.
MahanGM
3
Use a JSON_PARTIAL_OUTPUT_ON_ERRORopção para ver o problema (por exemplo, o campo com UTF8 será nulo).
Peter Krauss

Respostas:

255

Bem depois de 2 horas de escavação (cf. Edits)

Eu descobri a seguir:

  • No meu caso é um problema de codificação
  • mb_detect_encoding retorna provavelmente uma resposta com falha, algumas strings provavelmente não eram UTF-8
  • usar utf8_encode()nessas cordas resolveu meu problema, mas veja a nota abaixo

Esta é uma função recursiva que pode forçar a conversão para UTF-8 de todas as strings contidas em uma 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;
}

Use-o simplesmente assim:

echo json_encode(utf8ize($data));

Nota: utf8_encode () codifica a string ISO-8859-1 para UTF-8 de acordo com os documentos, portanto, se você não tiver certeza da codificação de entrada, iconv () ou mb_convert_encoding () podem ser as melhores opções, conforme observado nos comentários e outras soluções.

Matthieu Riegler
fonte
4
Obrigado pela sua solução ... no entanto, uma observação lateral: altere o } else {para } else if (is_string ($d)) {; caso contrário, você mudará tudo para strings (por exemplo, INTse tornará um STRING).
Paul Peelen
3
Você acabou de salvar minha vida. Eu estava quase terminando tudo até que encontrei esta função !! Obrigado.
silversunhunter
2
WTF! Obrigado por compartilhar sua solução. Posso ver que isso teria levado muito trabalho para descobrir, e sou grato a você por fazer isso e compartilhar.
kris
1
Depois de três dias de depuração, posso beijar você agora.
AJB
2
Se estiver lendo do banco de dados, use apenas $ conn-> set_charset ("utf8");
Andrew Briggs de
36

Matthieu Riegler apresentou uma solução realmente boa, mas eu tive que modificá-la ligeiramente para lidar com objetos também:

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

Mais uma nota: json_last_error () pode ser útil na depuração de funções json_encode () / json_encode ().

Adam Bubela
fonte
Não deveria ser em elseifvez de else if? (ou seja, sem espaço em branco).
Uwe Keim
2
@UweKeim de acordo com a documentação do PHP "elseif e senão se será considerado exatamente o mesmo ao usar chaves" o que significa que eles são equivalentes desde que você não use a notação de dois pontos, por exemploif(): elseif:
Adam Bubela
1
Bom trabalho. PHP é lixo e caras como você evitam que ele vá para o lixo.
Lonnie Melhor de
Você deve inserir um else if(is_int($d)||is_bool($d)) return $d;antes do último porque:{"success":true, "message":"Ⲃⲟⲟ𝓵ⲉⲁⲛ ⲁⲛⲇ Ⲓⲛϯⲉ𝓰ⲉꞅ𝛓"}
David Refoua
Assim como @ paul-peelen recomendado para @matthieu-riegler: Altere o último elsepor else if(is_string ($d)); caso contrário, você mudará tudo para strings (por exemplo, INTse tornará um STRING).
Bruno Serrano,
30

Para mim, a resposta para este problema foi a configuração charset=utf8na minha conexão PDO.

$dbo = new PDO('mysql:host=localhost;dbname=yourdb;charset=utf8', $username, $password);
fayd
fonte
2
Ou em funções mysqli: mysqli_set_charset ($ connection, "utf8");
user18099
Essa foi a dica para minha solução. Causa pouco diferente da conexão msqli. Basta ligar $mysqli->set_charset("utf8");depois de fazer o manuseio do banco de dados.
MaggusK
Use utf8mb4em versões recentes do MySQL. utf8está obsoleto.
Dharman
10

Adam Bubela também apresentou uma solução muito boa que me ajudou a resolver meu problema, e aqui está a função 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
fonte
1
Eu gosto deste porque preserva as chaves.
dev0
7

Eu tenho exatamente o mesmo problema no PHP 5.6. Eu uso Open Server + Nginx no Windows 7. Todos os conjuntos de caracteres são definidos como UTF-8. Em tese, de acordo com a documentação oficial , bandeira

JSON_UNESCAPED_UNICODE

deve resolver isso. Infelizmente este não é o meu caso. Eu não sei porque. Todos os snippets acima não resolvem meu problema, portanto, encontrei minha própria implementação. Eu acredito que pode ajudar alguém. Pelo menos, as letras russas passam no teste.

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
fonte
4

Esta resposta aceita funciona. Mas caso você esteja obtendo seus dados do MySQL (como eu estava), existe uma maneira mais fácil.

Depois de abrir o banco de dados, antes de consultar, você pode definir o conjunto de caracteres usando mysqli da seguinte maneira:

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

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

Kholofelo
fonte
4

Eu encontrei esse problema em um servidor executando uma versão anterior do PHP (5.2). Eu estava usando a sinalização JSON_FORCE_OBJECT e, aparentemente, ela não é compatível até 5.3

Portanto, se você estiver usando esse sinalizador, verifique sua versão!

Uma solução alternativa parece ser apenas transmitir para um objeto antes da codificação, como:

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

A devolução de mb_detect_encodingpode não estar correta:

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

Dependendo da ordem de detecção padrão, o acima pode retornar resultados diferentes, portanto, a codificação está sendo relatada erroneamente como UTF-8. ( Aqui está um exemplo maior .)

É provável que seus dados não estejam codificados como UTF-8, portanto, json_encodeestão retornando false. Você deve procurar converter suas strings em UTF-8 antes da codificação 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
fonte
3

Estava obtendo dados de ob_get_clean () e tive o mesmo problema, mas as soluções acima não funcionam para mim. No meu caso a solução foi esta, talvez ajude alguém.

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

usar utf8_encode () nessa string resolveu meu problema.

mobizen
fonte
1

Eu melhorei a resposta de Adam Bubela. Eu simplesmente odeio quando os blocos não são fechados por {e}. É mais limpo e você não introduz bugs ou talvez seja porque eu usei Perl no passado :)

<?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
fonte
0

Se você obtiver esses dados de um banco de dados, use mysqli_set_charset($connection, "utf8");em conexão ao obter os parâmetros do banco de dados

raul garcia de la fuente
fonte
0

este problema surge às vezes - você não está passando o controle de acesso do cabeçalho.

No meu caso, se foi adicionado algum eco antes de json_encode. Estava mostrando resultado, caso contrário, uma página em branco estava chegando.

Eu adicionei

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

e meu problema resolvido.

Anup
fonte