Dlaczego json_encode miałby zwrócić pusty ciąg

108

Mam prostą strukturę php z 3 zagnieżdżonymi tablicami.

Nie używam konkretnych obiektów i sam buduję tablice z 2 zagnieżdżonymi pętlami.

Oto przykład zmiennej var_dump tablicy, którą chcę przekonwertować na 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

W innym skrypcie mam podobną strukturę i json_encodedziała dobrze. Więc nie rozumiem, dlaczego json_encodetu nie zadziała.

Edycja: wydaje się, że wystąpił problem z kodowaniem. Kiedy mb_detect_encodingzwraca ASCII, json_encodedziała, ale kiedy zwraca UTF8, już nie działa.

Edit2: json_last_error()zwraca, JSON_ERROR_UTF8co oznacza: zniekształcone znaki UTF-8, prawdopodobnie nieprawidłowo zakodowane .

Matthieu Riegler
źródło
Podręcznik PHP mówi This function only works with UTF-8 encoded data., że nie powinno być żadnych problemów z kodowaniem.
MahanGM,
13
Spróbuj użyć utf8_encode()na swoich namepolach tablicy przed przekazać ciąg json_encode().
MahanGM,
Dzięki ! Właśnie doszedłem do tego rozwiązania, które rozwiązało mój problem.
Matthieu Riegler
Tak, widziałem odpowiedź. Powodzenia.
MahanGM,
3
Użyj JSON_PARTIAL_OUTPUT_ON_ERRORopcji, aby zobaczyć problem (np. Pole z UTF8 będzie puste).
Peter Krauss

Odpowiedzi:

255

Dobrze po 2 godzinach kopania (por. Edycje)

Dowiedziałem się, co następuje:

  • W moim przypadku jest to problem z kodowaniem
  • mb_detect_encoding zwraca prawdopodobnie błędną odpowiedź, niektóre łańcuchy prawdopodobnie nie były w UTF-8
  • użycie utf8_encode()na tych ciągach rozwiązało mój problem, ale patrz uwaga poniżej

Oto funkcja rekurencyjna, która może wymusić konwersję do UTF-8 wszystkich ciągów zawartych w tablicy:

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

Użyj go po prostu w ten sposób:

echo json_encode(utf8ize($data));

Uwaga: utf8_encode () koduje ciąg ISO-8859-1 do UTF-8 zgodnie z dokumentacją, więc jeśli nie masz pewności co do kodowania wejściowego iconv () lub mb_convert_encoding (), mogą być lepsze opcje, jak zauważono w komentarzach i innych rozwiązaniach.

Matthieu Riegler
źródło
4
Dzięki za rozwiązaniem ... Jednak jeden marginesie: zmiana } else {celu } else if (is_string ($d)) {; w przeciwnym razie zmienisz wszystko na łańcuchy (np. INTstanie się a STRING).
Paul Peelen
3
Właśnie uratowałeś mi życie. Miałem już wszystko zakończyć, dopóki nie znalazłem tej funkcji! Dziękuję Ci.
silversunhunter
2
WTF! Dziękujemy za udostępnienie rozwiązania. Widzę, że zajęłoby to dużo czasu, aby to rozgryźć, i jestem wdzięczny za to, że to zrobiłeś i udostępniłeś.
kris
1
Po trzech dniach debugowania mógłbym cię teraz pocałować.
AJB,
2
Jeśli czytasz z bazy danych, po prostu użyj, $ conn-> set_charset ("utf8");
Andrew Briggs
36

Matthieu Riegler zaprezentował naprawdę dobre rozwiązanie, ale musiałem je nieco zmodyfikować, aby obsługiwać obiekty:

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

Jeszcze jedna uwaga: json_last_error () może być pomocna w debugowaniu funkcji json_encode () / json_encode ().

Adam Bubela
źródło
Czy nie powinno być elseifzamiast tego else if? (tj. nie puste).
Uwe Keim
2
@UweKeim zgodnie z dokumentacją PHP „elseif i else if będzie traktowane dokładnie tak samo, gdy używasz nawiasów klamrowych”, co oznacza, że ​​są one równoważne, o ile nie używasz notacji z dwukropkiem, np.if(): elseif:
Adam Bubela
1
Dobra robota. PHP to śmieć i ludzie tacy jak ty nie pozwalają mu trafić na śmietnik.
Lonnie Best
Powinieneś wstawić else if(is_int($d)||is_bool($d)) return $d;przed ostatnią inną, ponieważ:{"success":true, "message":"Ⲃⲟⲟ𝓵ⲉⲁⲛ ⲁⲛⲇ Ⲓⲛϯⲉ𝓰ⲉꞅ𝛓"}
David Refoua
Tak jak @ paul-peelen polecono @ matthieu-riegler: Zmień ostatnie elsena else if(is_string ($d)); w przeciwnym razie zmienisz wszystko na łańcuchy (np. INTstanie się a STRING).
Bruno Serrano
30

Dla mnie odpowiedzią na ten problem było ustawienie charset=utf8w moim połączeniu PDO.

$dbo = new PDO('mysql:host=localhost;dbname=yourdb;charset=utf8', $username, $password);
fayd
źródło
2
Lub w funkcjach mysqli: mysqli_set_charset ($ connection, "utf8");
user18099
To była wskazówka do mojego rozwiązania. Trochę inna przyczyna połączenia msqli. Po prostu zadzwoń $mysqli->set_charset("utf8");po wykonaniu obsługi bazy danych.
MaggusK
Użyj utf8mb4w najnowszych wersjach MySQL. utf8jest przestarzały.
Dharman
10

Adam Bubela również zaprezentował naprawdę dobre rozwiązanie, który pomógł mi rozwiązać mój problem, a oto uproszczona funkcja:

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
źródło
1
Podoba mi się ten, ponieważ zachowuje klucze.
dev0,
7

Mam dokładnie ten sam problem w PHP 5.6. Używam Open Server + Nginx w Windows 7. Wszystkie zestawy znaków są ustawione na UTF-8. Teoretycznie według oficjalnej dokumentacji flaga

JSON_UNESCAPED_UNICODE

powinien rozwiązać ten problem. Niestety nie jest to mój przypadek. Nie wiem dlaczego. Wszystkie powyższe fragmenty nie rozwiązują mojego problemu, dlatego znalazłem własną implementację. Myślę, że to mogłoby komuś pomóc. Przynajmniej rosyjskie litery zdają egzamin.

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
źródło
4

Ta zaakceptowana odpowiedź działa. Ale w przypadku, gdy otrzymujesz dane z MySQL (tak jak ja), jest łatwiejszy sposób.

Po otwarciu bazy danych przed zapytaniem możesz ustawić zestaw znaków za pomocą mysqli w następujący sposób:

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

LUB

/* 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
źródło
4

Napotkałem ten problem na serwerze ze starszą wersją PHP (5.2). Używałem flagi JSON_FORCE_OBJECT i najwyraźniej nie jest ona obsługiwana do 5.3

Więc jeśli używasz tej flagi, sprawdź swoją wersję!

Wydaje się, że obejście polega na rzutowaniu na obiekt przed kodowaniem, na przykład:

json_encode((object)$myvar);
Shane N.
źródło
3

Zwrot mb_detect_encodingmoże być nieprawidłowy:

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

W zależności od domyślnej kolejności wykrywania powyższe może zwracać różne wyniki, więc kodowanie jest fałszywie zgłaszane jako UTF-8. ( Oto większy przykład .)

Prawdopodobnie Twoje dane nie są zakodowane jako UTF-8, więc json_encodepowracają false. Powinieneś spojrzeć na konwersję ciągów do UTF-8 przed kodowaniem 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
źródło
3

Otrzymywałem dane z ob_get_clean () i miałem ten sam problem, ale powyższe rozwiązania nie działają dla mnie. W moim przypadku rozwiązanie było takie, może komuś pomoże.

$var = mb_convert_encoding($var, 'UTF-8');
zdeniiik
źródło
2

użycie utf8_encode () na tych ciągach rozwiązało mój problem.

mobizen
źródło
1

Poprawiłem odpowiedź Adama Bubeli. Po prostu nienawidzę, kiedy bloki nie są zamykane przez {i}. Jest czystszy i nie wprowadzasz błędów, a może to dlatego, że używałem Perla w przeszłości :)

<?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
źródło
0

Jeśli otrzymujesz te dane z bazy danych, użyj mysqli_set_charset($connection, "utf8");w połączeniu, gdy pobierzesz parametry z bazy danych

raul garcia de la fuente
źródło
0

ten problem pojawia się czasami - nie przekazujesz kontroli dostępu do nagłówka.

W moim przypadku, jeśli dodano jakiekolwiek echo przed json_encode. Pokazał wynik, w przeciwnym razie nadchodziła pusta strona.

dodałem

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

i mój problem został rozwiązany.

Anup
źródło