为什么json_encode会返回一个空字符串?

133
我有一个简单的php结构,其中包含3个嵌套数组。
我不使用特定的对象,而是使用2个嵌套循环自己构建数组。
以下是我想要转换为Json的数组的var_dump示例。
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时,它就无法工作了。
编辑2:json_last_error()返回JSON_ERROR_UTF8,这意味着:UTF-8字符格式错误,可能编码不正确

PHP 手册中说“此函数仅适用于 UTF-8 编码的数据”,因此编码不应该有任何问题。 - MahanGM
14
在将字符串传递给json_encode()之前,尝试对您的name数组字段使用utf8_encode() - MahanGM
谢谢!我刚刚自己想到了这个解决方案,解决了我的问题。 - Matthieu Riegler
是的,我看到了答案。祝你好运。 - MahanGM
3
使用 JSON_PARTIAL_OUTPUT_ON_ERROR 选项 查看问题(例如,带有 UTF8 的字段将为 null)。 - Peter Krauss
听起来,如果输入有误,它应该返回false。从文档中可以看出:成功时返回JSON编码的字符串,失败时返回false。但是没有人回答为什么返回空字符串而不是false。 - Phil
14个回答

298

经过两个小时的查找(参见编辑),我发现以下内容:

  • 在我的情况下,这是一个编码问题
  • 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(),详情请见评论和其他解决方案。


我应该补充一下,这个问题是在我从Windows服务器备份并迁移到Ubuntu服务器后出现的。肯定是备份文件中出了问题。 - AJB
使用JSON_PARTIAL_OUTPUT_ON_ERROR选项来查看问题(例如,带有UTF8的字段将为空)。 - Peter Krauss
3
如果从数据库读取内容,只需使用 $conn->set_charset("utf8");。该语句用于设置数据库连接的字符集为 utf8,以正确处理中文等非 ASCII 字符。 - Andrew Briggs
1
PHP开发人员应该在命令参数中嵌入这个utf8选项! - Heitor
1
谢谢您。如果您需要更改外国字符,请将以下代码: return utf8_encode($d); 更改为: return iconv(mb_detect_encoding($d), "UTF-8", $d); - dbx
显示剩余2条评论

40

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_decode() 函数。


应该使用elseif而不是else if吧?(即没有空格)。 - Uwe Keim
2
根据PHP文档,"elseif和else if只有在使用花括号时才被视为完全相同",这意味着只要不使用冒号符号,它们就是等效的,例如if(): elseif: - Adam Bubela
你应该在最后一个else之前插入else if(is_int($d)||is_bool($d)) return $d;,因为:{"success":true, "message":"Ⲃⲟⲟⲉⲁⲛ ⲁⲛⲇ Ⲓⲛϯⲉⲉꞅ"} - David Refoua
就像 @paul-peelen 建议 @matthieu-riegler 的那样:将最后一个 else 改为 else if(is_string ($d));否则,您将把所有内容都更改为字符串(例如,INT 将变成 STRING)。 - Bruno Serrano
此外,json_last_error_msg()函数将返回一个更加用户友好的消息,解释为什么会出现失败。 - John Langford
那个最后的 return $d; 语句将永远不会被调用。 - Kae Verens

40

对我来说,解决这个问题的答案是在我的PDO连接中设置charset=utf8

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

5
在mysqli函数中,设置字符集的方法是:mysqli_set_charset($connection, "utf8");。 - user18099
这是我解决问题的提示。由于msqli连接有点不同,只需在处理数据库后调用$mysqli->set_charset("utf8");即可。 - MaggusK
2
请在最新的MySQL版本中使用utf8mb4utf8已经过时了。 - Dharman

10

Adam Bubela提供了一个非常好的解决方案,帮助我解决了我的问题,这是简化后的函数:

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
我喜欢这个,因为它保留了键。 - dev0

7
我在使用PHP 5.6时遇到了完全相同的问题。 我在Windows 7上使用Open Server + Nginx,所有字符集都设置为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;
}

6

这个被采纳的答案是可行的。但是如果你的数据来自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/zh/mysqli.set-charset.php

这是一个关于mysqli_set_charset函数的官方文档链接。该函数用于设置数据库连接的字符集,以正确地显示和处理来自数据库的数据。您可以在链接中找到更多有关此函数的详细信息。


注意:$mysqli是您创建MySQL连接时填充的变量名称。您的变量名称可能会有所不同。例如:$mysqli = new mysqli($host, $user, $password, $db, $port);此外,如上面的评论中所提到的,建议在新版本的MySQL中使用utf8mb4而不是utf8。 - Brian Swift

5

我在一台运行较旧版本 PHP (5.2) 的服务器上遇到了这个问题。我正在使用 JSON_FORCE_OBJECT 标志,但显然在 5.3 版本之前不支持该标志。

因此,如果您正在使用该标志,请确保检查您的版本!

一个解决方法似乎是在编码之前将其转换为对象,例如:

json_encode((object)$myvar);

4

我从 ob_get_clean() 函数中获得数据时,也遇到了相同的问题,但是以上解决方案对我无效。在我的情况下,解决方案是这样的,也许会帮助别人。

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

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。在JSON编码之前,你应该考虑将字符串转换为UTF-8格式。
$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);

2

由于输出最终将成为JSON(字符串),因此这里有一个可以处理各种变量类型的函数:

    function utf8ize($arg)
    {
        if (is_array($arg))
            foreach ($arg as $k => $v)
                $arg[$k] = utf8ize($v);
         else if(is_object($arg))
            return utf8ize((array) $arg);
         else
            return utf8_encode(strval($arg));
        return $arg;
    }

这个答案是基于Matthieu Riegler和Adam Bubela的答案进行改进的,但这个答案可以处理所有类型的变量/数据类型(例如:资源,如文件句柄/流/phpgd/sql连接)。
当参数是对象时,我使用了将其强制转换为数组以防止出现错误,如果对象属性是只读或私有/受保护的情况。
此外,在else语句中,我使用了strval来处理其他数据类型,如资源。
我使用这个函数来“强制”将变量转换为字符串(例如:在var_dump-ing或记录到文本文件时)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接