在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个回答

3
另一种简单的方法
function is_json($str)
{
    return is_array(json_decode($str,true));
}

1
这不正确。任何PHP类型都可以编码为JSON,例如对象、字符串等,而json_decode函数应该返回它们。 只有在解码数组和其他变量类型时才是正确的。 - Chaoix
@Chaoix 使用json_decode($str,true)使其将对象转换为数组,因此它将通过is_array检查。但是,您对于字符串、整数等是正确的。 - Paul Phillips
我明白你在json_encode的第二个参数上的意思。但我仍然认为@Ahad Ali的解决方案在输入和算法中只执行一次json_decode方面更好。 - Chaoix

2
应该是这样的:

应该是类似这样的:

 function isJson($string)
 {
    // 1. Speed up the checking & prevent exception throw when non string is passed
    if (is_numeric($string) ||
        !is_string($string) ||
        !$string) {
        return false;
    }

    $cleaned_str = trim($string);
    if (!$cleaned_str || !in_array($cleaned_str[0], ['{', '['])) {
        return false;
    }

    // 2. Actual checking
    $str = json_decode($string);
    return (json_last_error() == JSON_ERROR_NONE) && $str && $str != $string;
}

单元测试

public function testIsJson()
{
    $non_json_values = [
        "12",
        0,
        1,
        12,
        -1,
        '',
        null,
        0.1,
        '.',
        "''",
        true,
        false,
        [],
        '""',
        '[]',
        '   {',
        '   [',
    ];

   $json_values = [
        '{}',
        '{"foo": "bar"}',
        '[{}]',
        '  {}',
        ' {}  '
    ];

   foreach ($non_json_values as $non_json_value) {
        $is_json = isJson($non_json_value);
        $this->assertFalse($is_json);
    }

    foreach ($json_values as $json_value) {
        $is_json = isJson($json_value);
        $this->assertTrue($is_json);
    }
}

我喜欢你检查是否为字符串的方式。与第一个解决方案结合使用,可以避免如果字符串是数组或对象而出现 ErrorException - sykez
我很高兴你能写代码来加速内部ph函数。我想我的电脑在尝试json_decode一个数字时崩溃了。这200行代码肯定比汇编代码少得多。你能用0和1写代码吗?我只是和你开个玩笑,我的朋友。我的论点以一个笑话的形式表达,你真的认为并且基准测试了所有这么多的代码,才能比C语言内部编写的Php更快吗?这个函数有各种断点,你浪费的人力时间比你数学上只会增加CPU时间更多,如果有任何区别的话。 - Neo
我已经为您找到了源代码:https://github.com/php/php-src/blob/master/ext/json/json.c - Neo
该函数本身只有18行代码。即使包括单元测试,也只有大约60行。我敢肯定你是个开玩笑的人。 其次,它是纯PHP编写的,以解决我们这里的问题,而且易于理解和实现。我敢肯定每次调用该函数所需的时间仅为毫秒级别。 第三,除非您能提供更好的PHP解决方案,请这样做,否则在此提及C是不合适的,或者您只是炫耀自己的C技能吗? - Tinh Dang
看到有人拿5年前的解决方案和最近几个月才在PHP 8.3中添加的用C语言直接实现在PHP核心中的函数进行比较,然后谈论无关的话题,真是太奇怪了。不管怎样,请继续添加新答案,并别忘了提到从8.3开始我们可以使用json_validate并使用你那里的某种幽默方式给世界带来欢乐。 - Tinh Dang
你的一些 $non_json_values 是完全有效的 JSON。 - undefined

2

您好,这是我图书馆中的一个小片段。在这个条件语句中,我只是检查数据是否为JSON格式,如果正确解码则返回它,请注意性能用途的substr使用(我还没有看到任何以{或[开头的JSON文件)。

$input=trim($input);
if ((substr($input, 0, 1) == '{' && substr($input, -1) == '}') or (substr($input, 0, 1) == '[' && substr($input, -1) == ']')) {
    $output = json_decode($input, 1);
    if (in_array(gettype($output),['object','array'])) {
        #then it's definitely JSON
    }
}

这个问题已经有34个答案发布了,其中许多人也认为JSON必须表示为数组或对象(这是错误的信念)。这个答案与其他三十多个答案有什么不同吗? - miken32
1
我相信这个答案有很多好处,对于大多数使用情况,我们已经知道我们期望一个json字符串,所以这是检查花括号,如果它们没有被找到,就不需要进行json_decode。我给你点赞。 - Oliver M Grech

1

我不了解我方案的性能或优雅程度,但这就是我正在使用的:

if (preg_match('/^[\[\{]\"/', $string)) {
    $aJson = json_decode($string, true);
    if (!is_null($aJson)) {
       ... do stuff here ...
    }
}

自从我的所有JSON编码字符串都以{"开头,因此可以使用正则表达式进行测试。 我对正则表达式不熟悉,所以可能有更好的方法来解决这个问题。 另外:strpos()可能会更快。

只是想要贡献一点我的见解。

P.S. 刚刚更新了正则表达式字符串为/^[\[\{]\"/以查找JSON数组字符串。 因此,现在它会查找字符串开头的["或{"。


1

更新:json_validate() 将在 PHP 8.3 中上线

提醒:

我正在致力于一项 RFC,旨在添加一个新的函数到 PHP 中,该函数能够仅验证 JSON 字符串而不生成对象/数组。

为什么需要一个仅验证的函数?因为 json_decode() 在解析 JSON 字符串时会创建一个数组/对象,从而影响使用的内存量;这意味着验证 JSON 字符串时可能会达到最大内存限制。

举个例子,看看这段代码 performance_test_json_validate()_vs_json_decode()

在这个测试中,我们可以看到新函数json_validate()用0 MB来验证 JSON 字符串,而json_decode()需要109 MB(因为它在解析时创建了一个内存数组/对象)。

目前这还是一个正在进行中的工作,但我想发表一下我的看法(不是关于是否值得拥有它,而是从技术角度来看)。

Github: https://github.com/php/php-src/pull/9399

RFC(正在进行中):https://wiki.php.net/rfc/json_validate

期待您对此的意见和支持。

提前致谢。


1
function isJson($string) {
    $obj = json_decode($string);
    return json_last_error() === JSON_ERROR_NONE && gettype($obj ) == "object";
}

这个可以工作,但对于数字不会返回true

新更新

上述解决方案如果JSON很长并且您不需要使用$obj,则性能不佳

如果您只想检查,最好使用以下函数

function isJson($string) {
    if(is_numeric($string)) return false;
    json_decode($string);
    return json_last_error() === JSON_ERROR_NONE;
}

1
在我看来,如果您还想实际使用解码后的对象,最好的解决方案应该是JSON。 - Dennis Richter
你是对的。我更新了答案。 - milad nazari
一个数字是有效的JSON。 - undefined

0

这个答案的基础上,进一步探讨以下问题:

<?php

    $json = '[{"user_id":13,"username":"stack"},{"user_id":14,"username":"over"}]';
    //$json = '12';

    function isJson($string) {
        json_decode($string);
        if(json_last_error() == JSON_ERROR_NONE) {
            if(substr($string,0,1) == '[' && substr($string,-1) == ']') { return TRUE; }
            else if(substr($string,0,1) == '{' && substr($string,-1) == '}') { return TRUE; }
            else { return FALSE; }
        }
    }

    echo isJson($json);
?>

3
如果在子字符串检查中发现错误,执行解码之前应该进行子字符串检查以节省时间,这样做会更快。我认为4个子字符串检查比一个json_decode更快,但如果有人能支持我的假设,我会欣赏任何想法。 - Mark
那是一个合理的论点。我不知道涉及的处理时间,但如果它更快,那么是的。 - Robert Johnstone

0
$r = (array)json_decode($arr);
if(!is_array($r) || count($r) < 1) return false;

5
这个问题已经有了三十个回答,其中一个回答获得了超过600个支持。这并不意味着新的回答不能为对话带来新的贡献。但是这也意味着你的回答会受益于额外的解释。你的回答有什么不同之处?为什么或在什么情况下,有人可能更喜欢你的方法?语言上是否有什么变化可以使你的方法有效或使以前的方法无效?请编辑你的回答以帮助区分你的方法和在过去十年中贡献的其他30个回答。 - Jeremy Caney
在当前版本的PHP中,is_array将始终返回false,因此应将其声明为数组,然后检查它是否具有值。此外,这也是一种更简短的方式。 - איש נחמד

0
另一个建议来自我 :)
function isJson(string $string) {
  return ($result = json_decode($string, true)) ? $result : $string;
}

0
如果本地文件 stations.json 无效、缺失或超过一个月,请执行某些操作。
if (!is_array(json_decode(@file_get_contents("stations.json"))) || time() > filemtime("stations.json") + (60*60*24*31)){
  // The json file is invalid, missing, or is more than 1 month old
  // Get a fresh version
} else {
  // Up to date
}

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