Почему json_encode возвращает пустую строку

108

У меня простая структура php с 3 вложенными массивами.

Я не использую отдельные объекты и сам строю массивы с двумя вложенными циклами.

Вот образец var_dump массива, который я хочу преобразовать в 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

В другом скрипте у меня аналогичная структура, и он json_encodeотлично работает. Поэтому я не понимаю, почему json_encodeздесь не работает.

Изменить: похоже, проблема с кодировкой. Когда mb_detect_encodingвозвращает ASCII, json_encodeработает, но когда он возвращает UTF8, он больше не работает.

Edit2: json_last_error()возвращает, JSON_ERROR_UTF8что означает: искаженные символы UTF-8, возможно, неправильно закодированные .

Матье Риглер
источник
В руководстве по PHP сказано, This function only works with UTF-8 encoded data.что проблем с кодировкой быть не должно.
MahanGM
13
Попробуйте использовать поля utf8_encode()своего nameмассива, прежде чем передавать строку json_encode().
MahanGM
Спасибо ! Я просто пришел к этому решению, которое решило мою проблему.
Matthieu Riegler
Да, увидел ответ. Удачи.
MahanGM
3
Используйте JSON_PARTIAL_OUTPUT_ON_ERRORопцию, чтобы увидеть проблему (например, поле с UTF8 будет пустым).
Питер Краусс

Ответы:

255

Хорошо после 2 часов копания (см. Правки)

Выяснил следующее:

  • В моем случае это проблема с кодировкой
  • mb_detect_encoding возвращает, вероятно, ошибочный ответ, некоторые строки, вероятно, не были UTF-8
  • используя utf8_encode()на те строки решить мою проблему, но смотри примечание ниже

Вот рекурсивная функция, которая может принудительно преобразовать в UTF-8 все строки, содержащиеся в массиве:

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

Используйте это просто так:

echo json_encode(utf8ize($data));

Примечание: utf8_encode () кодирует строку ISO-8859-1 в UTF-8 в соответствии с документами, поэтому, если вы не уверены в кодировке ввода, iconv () или mb_convert_encoding () могут быть лучшими вариантами, как указано в комментариях и других решениях.

Матье Риглер
источник
4
Спасибо за ваше решение ... однако одно замечание: измените значение } else {на } else if (is_string ($d)) {; в противном случае вы все измените на строки (например INT, станет a STRING).
Paul Peelen 05
3
Ты только что спас мне жизнь. Я собирался все закончить, пока не нашел эту функцию !! Спасибо.
silversunhunter
2
WTF! Спасибо, что поделились своим решением. Я вижу, что для этого потребовалось бы много копать, и я благодарен вам за это и за то, что поделился.
Крис
1
После трех дней отладки я могу поцеловать тебя прямо сейчас.
AJB
2
При чтении из базы данных просто используйте, $ conn-> set_charset ("utf8");
Эндрю Бриггс
36

Matthieu Riegler представил действительно хорошее решение, однако мне пришлось немного изменить его для обработки объектов:

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

Еще одно замечание: json_last_error () может быть полезен при отладке функций json_encode () / json_encode ().

Адам Бубела
источник
Разве не должно быть elseifвместо else if? (т.е. без пробела).
Уве Кейм,
2
@UweKeim согласно документации PHP «elseif и else if будут считаться одинаковыми только при использовании фигурных скобок», что означает, что они являются эквивалентами, если вы не используете обозначение двоеточия, напримерif(): elseif:
Адам Бубела
1
Хорошая работа. PHP - это мусор, и ребята вроде вас не пускают его на свалку.
Lonnie Best
Вам следует вставить else if(is_int($d)||is_bool($d)) return $d;перед последним else по следующим {"success":true, "message":"Ⲃⲟⲟ𝓵ⲉⲁⲛ ⲁⲛⲇ Ⲓⲛϯⲉ𝓰ⲉꞅ𝛓"}
причинам
Точно так же, как @ paul-peelen рекомендовал @ matthieu-riegler: заменить последнее elseна else if(is_string ($d)); в противном случае вы все измените на строки (например INT, станет a STRING).
Бруно Серрано
30

Для меня ответом на эту проблему была установка charset=utf8в моем PDO-соединении.

$dbo = new PDO('mysql:host=localhost;dbname=yourdb;charset=utf8', $username, $password);
файд
источник
2
Или в функциях mysqli: mysqli_set_charset ($ connection, "utf8");
user18099
Это был намек на мое решение. Немного другая причина подключения msqli. Просто позвоните $mysqli->set_charset("utf8");после обработки своей базы данных.
MaggusK 01
Пожалуйста, используйте utf8mb4в последних версиях MySQL. utf8устарело.
Дхарман
10

Адам Бубела также представил действительно хорошее решение, которое помогло мне решить мою проблему, и вот упрощенная функция:

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
источник
1
Мне нравится этот, потому что он сохраняет ключи.
dev0 06
7

У меня точно такая же проблема на PHP 5.6. Я использую Open Server + Nginx в Windows 7. Все кодировки установлены на UTF-8. Теоретически, согласно официальной документации , флаг

JSON_UNESCAPED_UNICODE

должен решить это. К сожалению, это не мой случай. Я не знаю, почему. Все приведенные выше фрагменты не решают мою проблему, поэтому я нашел свою реализацию. Я считаю, что это могло кому-то помочь. По крайней мере, русские буквы проходят проверку.

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

Этот принятый ответ работает. Но если вы получаете данные из MySQL (как я), есть более простой способ.

После открытия базы данных перед запросом вы можете установить набор символов с помощью mysqli следующим образом:

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

ИЛИ

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

ССЫЛКА: http://php.net/manual/en/mysqli.set-charset.php

Холофело
источник
4

Я столкнулся с этой проблемой на сервере с более старой версией PHP (5.2). Я использовал флаг JSON_FORCE_OBJECT, и, по-видимому, он не поддерживается до версии 5.3.

Поэтому, если вы используете этот флаг, обязательно проверьте свою версию!

Обходной путь, кажется, просто приведен к объекту перед кодированием, например:

json_encode((object)$myvar);
Шейн Н
источник
3

Возврат mb_detect_encodingможет быть неверным:

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

В зависимости от порядка обнаружения по умолчанию, приведенное выше может возвращать разные результаты, поэтому кодировка ошибочно отображается как UTF-8. ( Вот более крупный пример .)

Вероятно, что ваши данные не закодированы как UTF-8, поэтому json_encodeвозвращается false. Вы должны посмотреть на преобразование ваших строк в UTF-8 перед кодированием 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
источник
3

Я получал данные от ob_get_clean () и имел ту же проблему, но приведенные выше решения не работают для меня. В моем случае решение было таким, может, это кому-то поможет.

$var = mb_convert_encoding($var, 'UTF-8');
здениик
источник
2

использование utf8_encode () в этой строке решило мою проблему.

Mobizen
источник
1

Я улучшил ответ Адама Бубелы. Я просто ненавижу, когда блоки не закрываются {и}. Он чище, и вы не вносите ошибок, или, может быть, я использовал Perl в прошлом :)

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

Если вы получаете эти данные из базы данных, используйте mysqli_set_charset($connection, "utf8");соединение при получении параметров из базы данных.

Рауль Гарсия де ла Фуэнте
источник
0

эта проблема возникает иногда - вы не передаете управление доступом к заголовку.

В моем случае, если мне было добавлено какое-либо эхо до json_encode. Он показывал результат, иначе появлялась пустая страница.

я добавил

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

и моя проблема решена.

Ануп
источник