在PHP中检查字符串是否为JSON的最快方法是什么?

543

我需要一种非常快的方法来检查一个字符串是否是JSON格式。我觉得下面的方法不是最好的:

function isJson($string) {
    return ((is_string($string) &&
            (is_object(json_decode($string)) ||
            is_array(json_decode($string))))) ? true : false;
}

有没有性能爱好者想要改进这个方法?


3
建议只使用一次json_decode,并检查其输入和返回值。 - user166390
8
那么,哪个是答案? - Farid Rn
12
这里的三元开关是多余的。你的语句已经被评估为布尔值。 - I wrestled a bear once.
1
接受 Lewis Donovan 的答案...它运行良好。 - Poonam Bhatt
显示剩余4条评论
38个回答

15
这样做就可以了:
function isJson($string) {
    $decoded = json_decode($string); // decode our JSON string
    if ( !is_object($decoded) && !is_array($decoded) ) {
        /*
        If our string doesn't produce an object or array
        it's invalid, so we should return false
        */
        return false;
    }
    /*
    If the following line resolves to true, then there was
    no error and our JSON is valid, so we return true.
    Otherwise it isn't, so we return false.
    */
    return (json_last_error() == JSON_ERROR_NONE);
}

if ( isJson($someJsonString) ) {
    echo "valid JSON";
} else {
    echo "not valid JSON";
}

如其他答案所示,json_last_error() 返回上次 json_decode() 的任何错误。然而,有些边缘用例仅使用该函数并不全面。例如,如果您对整数(例如:123)或没有空格或其他字符的数字字符串(例如:"123")进行 json_decode(),则 json_last_error() 函数将无法捕获错误。
为了解决这个问题,我添加了一个额外的步骤,确保我们的 json_decode() 的结果是对象或数组。如果不是,则返回 false
要查看此操作,请检查以下两个示例:

"hello" 是一个有效的 JSON,它既不是对象也不是数组,使用 json_last_error() 就足够了。 - JoniJnm
1
当您对字符串“hello”进行json_decode()时,json_last_error()将返回错误代码4。示例在此处:https://3v4l.org/lSsEo - Lewis Donovan
1
你的代码有误,hello 不是有效的 JSON,但是 "hello" 是。 - JoniJnm

13
使用以下类和PHPBench,达到了以下结果:
<?php

declare(strict_types=1);

/**
 * @Revs(1000)
 * @Iterations(100)
 */
class BenchmarkJson
{
    public function benchCatchValid(): bool
    {
        $validJson = '{"validJson":true}';
        try {
            json_decode($validJson, true, 512, JSON_THROW_ON_ERROR);
            return true;
        } catch(\JsonException $exception) {}
        return false;
    }

    public function benchCatchInvalid(): bool
    {
        $invalidJson = '{"invalidJson"';
        try {
            json_decode($invalidJson, true, 512, JSON_THROW_ON_ERROR);
            return true;
        } catch(\JsonException $exception) {}
        return false;
    }

    public function benchLastErrorValid(): bool
    {
        $validJson = '{"validJson":true}';
        json_decode($validJson, true);
        return (json_last_error() === JSON_ERROR_NONE);
    }

    public function benchLastErrorInvalid(): bool
    {
        $invalidJson = '{"invalidJson"';
        json_decode($invalidJson, true);
        return (json_last_error() === JSON_ERROR_NONE);
    }

    public function benchNullValid(): bool
    {
        $validJson = '{"validJson":true}';
        return (json_decode($validJson, true) !== null);
    }

    public function benchNullInvalid(): bool
    {
        $invalidJson = '{"invalidJson"';
        return (json_decode($invalidJson, true) !== null);
    }
}


6 subjects, 600 iterations, 6,000 revs, 0 rejects, 0 failures, 0 warnings
(best [mean mode] worst) = 0.714 [1.203 1.175] 1.073 (μs)
⅀T: 721.504μs μSD/r 0.089μs μRSD/r: 7.270%
suite: 1343ab9a3590de6065bc0bc6eeb344c9f6eba642, date: 2020-01-21, stime: 12:50:14
+---------------+-----------------------+-----+------+-----+------------+---------+---------+---------+---------+---------+--------+-------+
| benchmark     | subject               | set | revs | its | mem_peak   | best    | mean    | mode    | worst   | stdev   | rstdev | diff  |
+---------------+-----------------------+-----+------+-----+------------+---------+---------+---------+---------+---------+--------+-------+
| BenchmarkJson | benchCatchValid       | 0   | 1000 | 100 | 2,980,168b | 0.954μs | 1.032μs | 1.016μs | 1.428μs | 0.062μs | 6.04%  | 1.33x |
| BenchmarkJson | benchCatchInvalid     | 0   | 1000 | 100 | 2,980,184b | 2.033μs | 2.228μs | 2.166μs | 3.001μs | 0.168μs | 7.55%  | 2.88x |
| BenchmarkJson | benchLastErrorValid   | 0   | 1000 | 100 | 2,980,184b | 1.076μs | 1.195μs | 1.169μs | 1.616μs | 0.083μs | 6.97%  | 1.54x |
| BenchmarkJson | benchLastErrorInvalid | 0   | 1000 | 100 | 2,980,184b | 0.785μs | 0.861μs | 0.863μs | 1.132μs | 0.056μs | 6.54%  | 1.11x |
| BenchmarkJson | benchNullValid        | 0   | 1000 | 100 | 2,980,168b | 0.985μs | 1.124μs | 1.077μs | 1.731μs | 0.114μs | 10.15% | 1.45x |
| BenchmarkJson | benchNullInvalid      | 0   | 1000 | 100 | 2,980,184b | 0.714μs | 0.775μs | 0.759μs | 1.073μs | 0.049μs | 6.36%  | 1.00x |
+---------------+-----------------------+-----+------+-----+------------+---------+---------+---------+---------+---------+--------+-------+


结论:检查 JSON 是否有效的最快方法是返回 json_decode($json, true) !== null)

非常好的 :) 我很钦佩你 - Mahdi

9

检查JSON结果的简单方法是...

$result = @json_decode($json,true);
    if (is_array($result)) {
        echo 'JSON is valid';
    }else{
        echo 'JSON is not valid';
    }

6

或许你现在想要修改这个问题,因为8.3版本已经发布,并且有关于这个函数的文档。 - undefined

6

2023年的答案:

虽然PHP 8.3仍在开发中,但它将提供新的内存高效的json_validate()函数,您可以借助令人惊叹的Symfony Polyfill组件在旧版本的PHP(7.1及以上)中使用。

只需将以下软件包添加到您的项目中即可:

composer require symfony/polyfill-php83

并在您的应用程序中使用它:

if (json_validate($data)) {
  // do sometihng
}

通过这种方法,您可以在旧应用程序中使用新的PHP功能,并且在将来迁移到PHP 8.3时不需要进行任何代码更改,因为当可用时,您的代码将自动使用内置函数。


6

GuzzleHttp中:

/**
 * Wrapper for json_decode that throws when an error occurs.
 *
 * @param string $json    JSON data to parse
 * @param bool $assoc     When true, returned objects will be converted
 *                        into associative arrays.
 * @param int    $depth   User specified recursion depth.
 * @param int    $options Bitmask of JSON decode options.
 *
 * @return mixed
 * @throws \InvalidArgumentException if the JSON cannot be decoded.
 * @link http://www.php.net/manual/en/function.json-decode.php
 */
function json_decode($json, $assoc = false, $depth = 512, $options = 0)
{
    $data = \json_decode($json, $assoc, $depth, $options);
    if (JSON_ERROR_NONE !== json_last_error()) {
        throw new \InvalidArgumentException(
            'json_decode error: ' . json_last_error_msg());
    }

    return $data;
}

/**
 * Wrapper for JSON encoding that throws when an error occurs.
 *
 * @param mixed $value   The value being encoded
 * @param int    $options JSON encode option bitmask
 * @param int    $depth   Set the maximum depth. Must be greater than zero.
 *
 * @return string
 * @throws \InvalidArgumentException if the JSON cannot be encoded.
 * @link http://www.php.net/manual/en/function.json-encode.php
 */
function json_encode($value, $options = 0, $depth = 512)
{
    $json = \json_encode($value, $options, $depth);
    if (JSON_ERROR_NONE !== json_last_error()) {
        throw new \InvalidArgumentException(
            'json_encode error: ' . json_last_error_msg());
    }

    return $json;
}

6
//Tested thoroughly, Should do the job:
public static function is_json(string $json):bool
{
    json_decode($json);
    if (json_last_error() === JSON_ERROR_NONE) {
        return true;
    }
    return false;
}

5
我们需要检查传递的字符串是否为非数字,因为在这种情况下,json_decode不会引发任何错误。
function isJson($str) {
    $result = false;
    if (!preg_match("/^\d+$/", trim($str))) {
        json_decode($str);
        $result = (json_last_error() == JSON_ERROR_NONE);
    }

    return $result;
}

5

之前我只是检查了一个空值,但实际上这是错误的。

    $data = "ahad";
    $r_data = json_decode($data);
    if($r_data){//json_decode will return null, which is the behavior we expect
        //success
    }

上述代码在处理字符串时运行良好。然而,一旦我提供数字,它就会出错。例如:

    $data = "1213145";
    $r_data = json_decode($data);

    if($r_data){//json_decode will return 1213145, which is the behavior we don't expect
        //success
    }

为了解决这个问题,我所做的非常简单。
    $data = "ahad";
    $r_data = json_decode($data);

    if(($r_data != $data) && $r_data)
        print "Json success";
    else
        print "Json error";

不错的解决方案。很好地处理了打字问题! - Chaoix

3

我尝试了一些解决方案,但没有一个适合我。我尝试了这个简单的方法:

$isJson = json_decode($myJSON);

if ($isJson instanceof \stdClass || is_array($isJson)) {
   echo("it's JSON confirmed");
} else {
   echo("nope");
}

我认为这是一个不错的解决方案,因为没有第二个参数的JSON解码会生成一个对象。

编辑:如果您知道输入内容,可以根据需要调整此代码。在我的情况下,我知道我的Json以“{”开头,所以不需要检查它是否是一个数组。


你的JSON可能只是一个数组,如果是这样,它将是一个数组而不是stdClass。$foo = "[1, 1, 2, 3]"; var_dump(json_decode($foo)); =>array(4) { [0]=> int(1) [1]=> int(1) [2]=> int(2) [3]=> int(3) } - Misha Nasledov

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