json_decode 返回 JSON_ERROR_SYNTAX,但在线格式化工具显示 JSON 正常。

51

我遇到了一个非常奇怪的问题。

我有一个JSON webservice。

当我用这个网站http://www.freeformatter.com/json-formatter.html#ad-output检查它时,一切都是正常的。

但是当我使用以下代码加载我的JSON时:

  $data = file_get_contents('http://www.mywebservice');

if(!empty($data))
{

    $obj = json_decode($data);

 switch (json_last_error()) {
    case JSON_ERROR_NONE:
        echo ' - JSON_ERROR_NONE';
    break;
    case JSON_ERROR_DEPTH:
        echo ' - JSON_ERROR_DEPTH';
    break;
    case JSON_ERROR_STATE_MISMATCH:
        echo ' - JSON_ERROR_STATE_MISMATCH';
    break;
    case JSON_ERROR_CTRL_CHAR:
        echo ' -  JSON_ERROR_CTRL_CHAR';
    break;
    case JSON_ERROR_SYNTAX:
        echo "\r\n\r\n - SYNTAX ERROR \r\n\r\n";
    break;
    case JSON_ERROR_UTF8:
        echo ' - JSON_ERROR_UTF8';
    break;
    default:
        echo ' - Unknown erro';
    break;
}

我收到了错误提示:语法错误。

这并没有任何帮助,真是个噩梦。

我发现在使用PHP 5.5时,我可以使用这个函数:http://php.net/manual/en/function.json-last-error-msg.php

(但我还没有成功安装PHP 5.5,并且我不确定这个函数是否会给我更多的详细信息)


4
请展示 JSON。 - Niet the Dark Absol
JSON 请。我们需要你的 JSON。 - sybear
也许你应该让错误信息更加详细?例如,将 JSON 包含在错误消息中? - Mr. Llama
17个回答

94

我遇到了相同的问题,实际上有一些隐藏的字符是看不见的,你需要将它们移除。 这是一个适用于许多情况的全局代码:

<?php
$checkLogin = file_get_contents("http://yourwebsite.com/JsonData");

// This will remove unwanted characters.
// Check http://www.php.net/chr for details
for ($i = 0; $i <= 31; ++$i) { 
    $checkLogin = str_replace(chr($i), "", $checkLogin); 
}
$checkLogin = str_replace(chr(127), "", $checkLogin);

// This is the most common part
// Some file begins with 'efbbbf' to mark the beginning of the file. (binary level)
// here we detect it and we remove it, basically it's the first 3 characters 
if (0 === strpos(bin2hex($checkLogin), 'efbbbf')) {
   $checkLogin = substr($checkLogin, 3);
}

$checkLogin = json_decode( $checkLogin );
print_r($checkLogin);
?>

6
尊敬的先生,您不知道这对我有多大帮助。我到处寻找解决方案,差点就放弃了。先生,您是我的救星。 - SkyPunch
仍然在想为什么这不是PHP 7.0.x核心的一部分。(摊手) - George Onofrei
+1 表示 BOM,我没有想到。根据单一职责原则,如果字符串包含无效字符或 BOM(技术上不属于 JSON 文档本身),那么执行清理的目的不是此函数的目的!@GeorgeOnofrei - BenMorel
这在我的情况下有所帮助... for ($i = 0; $i <= 31; ++$i) { $checkLogin = str_replace(chr($i), "", $checkLogin); } 但是为什么呢? - Sampgun
我从支付网关API响应中得到了这个错误。这是语法错误的修复方法。具体来说,过滤掉“efbbbf”。 - JSG
有什么办法可以查看这个隐藏字符吗? - neobie

65

移除BOM(字节序标记)通常是您需要的解决方案:

function removeBOM($data) {
    if (0 === strpos(bin2hex($data), 'efbbbf')) {
       return substr($data, 3);
    }
    return $data;
}

如果有BOM的话,你不应该有它,但是即使它存在,也是看不到的!

参见W3C有关HTML中BOM的说明

如果有大量文件需要修复,请使用BOM Cleaner


4
我已经通过Notepad++从“Notepad++顶部菜单>格式>UTF-8 without BOM”将编码从“UTF-8”更改为“UTF-8 without BOM”。 - deadfish
在找到这个答案之前,我浪费了近1小时尝试无数方法...非常感谢! - dmmd

33

在json_decode之前,我通过添加stripslashes函数来解决了这个问题。

$data = stripslashes($data); 
$obj = json_decode($data);

这对我不起作用,就像“removeBOM()”答案一样。 - Ooker

7

在我的情况下:

json_decode(html_entity_decode($json_string));

对$json_string进行HTML实体解码并解析为JSON格式的数据。

7

为了将所有的事情整合在一起,我准备了一个带有解码自动纠正操作的JSON包装器。最新版本可以在我的GitHub Gist中找到。

abstract class Json
{
    public static function getLastError($asString = FALSE)
    {
        $lastError = \json_last_error();

        if (!$asString) return $lastError;

        // Define the errors.
        $constants = \get_defined_constants(TRUE);
        $errorStrings = array();

        foreach ($constants["json"] as $name => $value)
            if (!strncmp($name, "JSON_ERROR_", 11))
                $errorStrings[$value] = $name;

        return isset($errorStrings[$lastError]) ? $errorStrings[$lastError] : FALSE;
    }

    public static function getLastErrorMessage()
    {
        return \json_last_error_msg();
    }

    public static function clean($jsonString)
    {
        if (!is_string($jsonString) || !$jsonString) return '';

        // Remove unsupported characters
        // Check http://www.php.net/chr for details
        for ($i = 0; $i <= 31; ++$i)
            $jsonString = str_replace(chr($i), "", $jsonString);

        $jsonString = str_replace(chr(127), "", $jsonString);

        // Remove the BOM (Byte Order Mark)
        // It's the most common that some file begins with 'efbbbf' to mark the beginning of the file. (binary level)
        // Here we detect it and we remove it, basically it's the first 3 characters.
        if (0 === strpos(bin2hex($jsonString), 'efbbbf')) $jsonString = substr($jsonString, 3);

        return $jsonString;
    }

    public static function encode($value, $options = 0, $depth = 512)
    {
        return \json_encode($value, $options, $depth);
    }

    public static function decode($jsonString, $asArray = TRUE, $depth = 512, $options = JSON_BIGINT_AS_STRING)
    {
        if (!is_string($jsonString) || !$jsonString) return NULL;

        $result = \json_decode($jsonString, $asArray, $depth, $options);

        if ($result === NULL)
            switch (self::getLastError())
            {
                case JSON_ERROR_SYNTAX :
                    // Try to clean json string if syntax error occured
                    $jsonString = self::clean($jsonString);
                    $result = \json_decode($jsonString, $asArray, $depth, $options);
                    break;

                default:
                    // Unsupported error
            }

        return $result;
    }
}

示例用法:

$json_data = file_get_contents("test.json");
$array = Json::decode($json_data, TRUE);
var_dump($array);
echo "Last error (" , Json::getLastError() , "): ", Json::getLastError(TRUE), PHP_EOL;

我整天都在处理一个奇怪编码的包含 JSON 的文件,这个类终于让我得到了一个可用的 PHP 数组 - 谢谢! - Hill79
对我非常有效!非常感谢!! - Giuseppe

4

我有同样的问题,遇到了JSON_ERROR_CTRL_CHARJSON_ERROR_SYNTAX错误。
这是我的解决方法。

$content = json_decode(json_encode($content), true);

3

在尝试了所有的解决方案但没有结果后,这个解决方案对我有用。

希望它能帮助到某些人

$data = str_replace('&quot;', '"', $data);

1

我也遇到了同样的问题。我采取了以下步骤:

  1. changed the JSON text encoding

    $json = utf8_encode($json);
    
  2. I then viewed the plain text before decoding. I found crazy symbols like

    ï

  3. then I just stripped it off

    $json = str_replace(array('ï',''), '',$json);
    

    and I successfully decoded my JSON


1
我也遇到了这个问题,让我非常沮丧。在尝试了多种互联网上的解决方案后,我注意到文件的编码是UTF-8带BOM,因为var_dump()在JSON前面输出了一个奇怪的字符&#65279;
我将正在使用的sample.json文件从UTF-8带BOM转换为UTF-8... 在VS CODE中,将以下内容添加到您的settings.json中,或确保以下设置代码如下所示(以便默认情况下对您创建的任何文件进行UTF-8编码)。
"files.encoding": "utf8",

然后你会在VSCode工具栏上看到类似下面的截图。(为了使json_decode()正常工作,文件必须以UTF-8编码)

enter image description here

但在我的情况下,我创建的JSON文件具有UTF-8带BOM编码,这就是为什么当我执行json_decode($json, true)时返回null(当我执行var_dump(json_last_error_msg())时出现语法错误)。

enter image description here

  1. 点击UTF-8 with BOM,然后您将获得下拉菜单,
  2. 点击Save with Encoding
  3. 您应该会看到以下屏幕截图,然后点击UTF-8

enter image description here

这将使用UTF-8编码重新保存您的文件,然后您可以继续检查您的代码。 json_decode() 将正常工作。真不敢相信我花了几个小时来弄清楚可能出了什么问题。

编码愉快!


1
你没有展示你的JSON,但这听起来可能是参数中存在无效的UTF-8序列,大多数在线验证器都无法捕获它。 确保你的数据是UTF-8编码,并检查是否有外文字符。 你不需要PHP5来查看错误,请使用error_log()记录问题。

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