Как сохранить, чтобы json_encode () не сбрасывал строки с недопустимыми символами - PullRequest
34 голосов
/ 12 января 2011

Есть ли способ не дать json_encode() вернуть null для строки, которая содержит недопустимый (не UTF-8) символ?

В заднице отладка может быть затрудненасложная система.Было бы гораздо удобнее увидеть недопустимый символ или, по крайней мере, опустить его.json_encode()

Пример (в UTF-8):

$string = 
  array(utf8_decode("Düsseldorf"), // Deliberately produce broken string
        "Washington",
        "Nairobi"); 

print_r(json_encode($string));

Результат в

[null,"Washington","Nairobi"]

Желаемый результат:

["D�sseldorf","Washington","Nairobi"]

Примечание : Я не , чтобы заставить работать ломаные строки в json_encode ().Я ищу способы облегчить диагностику ошибок кодирования.null строка для этого не нужна.

Ответы [ 6 ]

41 голосов
/ 12 января 2011

php пытается выдать ошибку, но только если вы отключите display_errors . Это странно, потому что настройка display_errors предназначена только для контроля того, будут ли ошибки выводиться на стандартный вывод, а не на то, была ли вызвана ошибка. Я хочу подчеркнуть, что когда вы включаете display_errors, хотя вы можете видеть все виды других ошибок php, php не просто скрывает эту ошибку, даже не вызывает ее . Это означает, что он не будет отображаться ни в каких журналах ошибок, и не будут вызываться какие-либо пользовательские обработчики ошибок. Ошибка просто никогда не возникает.

Вот код, демонстрирующий это:

error_reporting(-1);//report all errors
$invalid_utf8_char = chr(193);

ini_set('display_errors', 1);//display errors to standard output
var_dump(json_encode($invalid_utf8_char));
var_dump(error_get_last());//nothing

ini_set('display_errors', 0);//do not display errors to standard output
var_dump(json_encode($invalid_utf8_char));
var_dump(error_get_last());// json_encode(): Invalid UTF-8 sequence in argument

Это странное и неудачное поведение связано с этой ошибкой https://bugs.php.net/bug.php?id=47494 и некоторыми другими, и не похоже, что она когда-либо будет исправлена.

Обойти:

Очистка строки перед передачей ее в json_encode может быть приемлемым решением.

$stripped_of_invalid_utf8_chars_string = iconv('UTF-8', 'UTF-8//IGNORE', $orig_string);
if ($stripped_of_invalid_utf8_chars_string !== $orig_string) {
    // one or more chars were invalid, and so they were stripped out.
    // if you need to know where in the string the first stripped character was, 
    // then see http://stackoverflow.com/questions/7475437/find-first-character-that-is-different-between-two-strings
}
$json = json_encode($stripped_of_invalid_utf8_chars_string);

http://php.net/manual/en/function.iconv.php

В инструкции написано

//IGNORE молча отбрасывает недопустимых в цель символов набор символов.

Итак, сначала удалив проблемные символы, в теории json_encode () не должен получить ничего, что он захлебнется и потерпит неудачу. Я не проверял, что вывод iconv с флагом //IGNORE полностью совместим с понятием json_encodes того, что представляют собой действительные символы utf8, так что покупатель остерегается ... поскольку могут быть крайние случаи, когда он все еще не работает. тьфу, я ненавижу проблемы с набором символов.

Редактировать
в php 7.2+ появились новые флаги для json_encode: JSON_INVALID_UTF8_IGNORE и JSON_INVALID_UTF8_SUBSTITUTE
Документации пока немного, но сейчас этот тест должен помочь вам понять ожидаемое поведение: https://github.com/php/php-src/blob/master/ext/json/tests/json_encode_invalid_utf8.phpt

И в php 7.3+ появился новый флаг JSON_THROW_ON_ERROR. См http://php.net/manual/en/class.jsonexception.php

6 голосов
/ 25 февраля 2011
$s = iconv('UTF-8', 'UTF-8//IGNORE', $s);

Это решило проблему. Я не уверен, почему парни из php не сделали жизнь проще, исправив json_encode().

В любом случае использование вышеупомянутого позволяет json_encode () создавать объект, даже если данные содержат специальные символы (например, шведские буквы).

Затем вы можете использовать результат в javascript без необходимости декодировать данные обратно в исходное кодирование (с escape(), unescape(), encodeURIComponent(), decodeURIComponent());

Я использую это так в php (smarty):

$template = iconv('UTF-8', 'UTF-8//IGNORE', $screen->fetch("my_template.tpl"));

Затем я отправляю результат в javascript и просто innerHTML готовый шаблон (html peace) в моем документе.

Просто указанная выше строка должна быть как-то реализована в json_encode(), чтобы она могла работать с любой кодировкой.

5 голосов
/ 08 марта 2013

Эта функция удалит все недопустимые символы UTF8 из строки:

function removeInvalidChars( $text) {
    $regex = '/( [\x00-\x7F] | [\xC0-\xDF][\x80-\xBF] | [\xE0-\xEF][\x80-\xBF]{2} | [\xF0-\xF7][\x80-\xBF]{3} ) | ./x';
    return preg_replace($regex, '$1', $text);
}

Я использую ее после преобразования документа Excel в json, поскольку документы Excel не гарантированно будут в UTF8.

Не думаю, что есть особенно разумный способ преобразования недопустимых символов в видимый, но действительный символ.Вы можете заменить недопустимые символы на U + FFFD, который является заменяющим символом в Юникоде , перевернув приведенное выше регулярное выражение, но это на самом деле не дает лучшего пользовательского опыта, чем просто удаление недопустимых символов.

3 голосов
/ 12 января 2011

Вам нужно знать кодировку всех строк, с которыми вы имеете дело, или вы попадаете в мир боли.

UTF-8 - это простая в использовании кодировка.Кроме того, JSON определен для использования UTF-8 (http://www.json.org/JSONRequest.html). Так почему бы не использовать его?

Краткий ответ: способ избежать отбрасывания строк json_encode () - убедиться, что они действительны в UTF-8.

1 голос
/ 07 марта 2013

Вместо использования функции iconv вы можете использовать json_encode с опцией JSON_UNESCAPED_UNICODE (> = PHP5.4.0)

Убедитесь, что в заголовке вашего php указано «charset = utf-8»file:

header ('Content-Type: application / json; charset = utf-8');

0 голосов
/ 13 марта 2019

для получения информационного уведомления об ошибках json мы используем этот помощник:

  • временно устанавливает специальный обработчик ошибок для отлова ошибок json для кодирования / декодирования
  • выдает RuntimeException при ошибке
<?php

/**
 * usage:
 * $json = HelperJson::encode(['bla'=>'foo']);
 * $array = HelperJson::decode('{"bla":"foo"}');
 * 
 * throws exception on failure
 * 
 */
class HelperJson {

    /**
     * @var array
     */
    static private $jsonErrors = [
            JSON_ERROR_NONE => '',
            JSON_ERROR_UTF8 => 'Malformed UTF-8 characters, possibly incorrectly encoded',
            JSON_ERROR_DEPTH => 'Maximum stack depth exceeded',
            JSON_ERROR_STATE_MISMATCH => 'Underflow or the modes mismatch',
            JSON_ERROR_CTRL_CHAR => 'Unexpected control character found',
            JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON',
    ];

    /**
     * ! assoc ! (reverse logic to php function)
     * @param string $jsonString
     * @param bool $assoc
     * @throws RuntimeException
     * @return array|null
     */
    static public function decode($jsonString, $assoc=true){

        HelperJson_ErrorHandler::reset(); // siehe unten
        set_error_handler('HelperJson_ErrorHandler::handleError');

        $result = json_decode($jsonString, $assoc);

        $errStr = HelperJson_ErrorHandler::getErrstr();
        restore_error_handler();

        $jsonError = json_last_error();
        if( $jsonError!=JSON_ERROR_NONE ) {
            $errorMsg = isset(self::$jsonErrors[$jsonError]) ? self::$jsonErrors[$jsonError] : 'unknown error code: '.$jsonError;
            throw new \RuntimeException('json decoding error: '.$errorMsg.' JSON: '.substr($jsonString,0, 250));
        }
        if( $errStr!='' ){
            throw new \RuntimeException('json decoding problem: '.$errStr.' JSON: '.substr($jsonString,0, 250));
        }
        return $result;
    }

    /**
     * encode with error "throwing"
     * @param mixed $data
     * @param int $options   $options=JSON_PRESERVE_ZERO_FRACTION+JSON_UNESCAPED_SLASHES : 1024 + 64 = 1088
     * @return string
     * @throws \RuntimeException
     */
    static public function encode($data, $options=1088){

        HelperJson_ErrorHandler::reset();// scheint notwendg da sonst bei utf-8 problemen nur eine warnung geflogen ist, die hier aber nicht durchschlug, verdacht der error handler macht selbst was mit json und reset damit json_last_error
        set_error_handler('HelperJson_ErrorHandler::handleError');

        $result = json_encode($data, $options);

        $errStr = HelperJson_ErrorHandler::getErrstr();
        restore_error_handler();

        $jsonError = json_last_error();
        if( $jsonError!=JSON_ERROR_NONE ){
            $errorMsg = isset(self::$jsonErrors[$jsonError]) ? self::$jsonErrors[$jsonError] : 'unknown error code: '.$jsonError;
            throw new \RuntimeException('json encoding error: '.$errorMsg);
        }
        if( $errStr!='' ){
            throw new \RuntimeException('json encoding problem: '.$errStr);
        }
        return $result;
    }

}

/**

HelperJson_ErrorHandler::install();
preg_match('~a','');
$errStr = HelperJson_ErrorHandler::getErrstr();
HelperJson_ErrorHandler::remove();

 *
 */
class HelperJson_ErrorHandler {

    static protected  $errno = 0;
    static protected  $errstr = '';
    static protected  $errfile = '';
    static protected  $errline = '';
    static protected  $errcontext = array();

    /**
     * @param int $errno
     * @param string $errstr
     * @param string $errfile
     * @param int $errline
     * @param array $errcontext
     * @return bool
     */
    static public function handleError($errno, $errstr, $errfile, $errline, $errcontext){
        self::$errno = $errno;
        self::$errstr = $errstr;
        self::$errfile = $errfile;
        self::$errline = $errline;
        self::$errcontext = $errcontext;
        return true;
    }

    /**
     * @return int
     */
    static public function getErrno(){
        return self::$errno;
    }
    /**
     * @return int
     */
    static public function getErrstr(){
        return self::$errstr;
    }
    /**
     * @return int
     */
    static public function getErrfile(){
        return self::$errfile;
    }
    /**
     * @return int
     */
    static public function getErrline(){
        return self::$errline;
    }
    /**
     * @return array
     */
    static public function getErrcontext(){
        return self::$errcontext;
    }
    /**
     * reset last error
     */
    static public function reset(){
        self::$errno = 0;
        self::$errstr = '';
        self::$errfile = '';
        self::$errline = 0;
        self::$errcontext = array();
    }

    /**
     * set black-hole error handler
     */
    static public function install(){
        self::reset();
        set_error_handler('HelperJson_ErrorHandler::handleError');
    }

    /**
     * restore previous error handler
     */
    static function remove(){
        restore_error_handler();
    }
}

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...