我需要一个非常非常快的方法来检查字符串是否为JSON。我觉得这不是最好的方式:
function isJson($string) {
return ((is_string($string) &&
(is_object(json_decode($string)) ||
is_array(json_decode($string))))) ? true : false;
}
有没有表演爱好者想改进这种方法?
我需要一个非常非常快的方法来检查字符串是否为JSON。我觉得这不是最好的方式:
function isJson($string) {
return ((is_string($string) &&
(is_object(json_decode($string)) ||
is_array(json_decode($string))))) ? true : false;
}
有没有表演爱好者想改进这种方法?
当前回答
更新:json_validate()将在PHP 8.3中上线
FYI:
我正在研究一个RFC,在php中添加一个新函数,它能够验证—只验证json字符串,而不生成对象/数组。
为什么是只验证的函数?因为json_decode()在解析json-string时创建了一个数组/对象,这会影响正在使用的内存量;这意味着在验证json字符串时可以达到最大内存限制。
为了给你一个概念,检查这段代码performance_test_json_validate()_vs_json_decode():
在该测试中,我们可以看到新函数json_validate()使用0 MB来验证json-string,而json_decode()需要109 MB(因为它在解析时在内存中创建了一个数组/对象)。
这是目前正在进行的工作,但我发布这篇文章是因为我对你对它的看法很感兴趣(我的意思是,从技术角度来看,不是你认为值得拥有它)。
Github: https://github.com/php/php-src/pull/9399
RFC(工作进行中):https://wiki.php.net/rfc/json_validate
期待您的意见/支持。
提前谢谢你。
其他回答
再扩展一下这个答案,下面的问题如何:
<?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);
?>
问题的答案
函数json_last_error返回JSON编码和解码过程中发生的最后一个错误。因此,检查有效JSON的最快方法是
// decode the JSON data
// set second parameter boolean TRUE for associative array output.
$result = json_decode($json);
if (json_last_error() === JSON_ERROR_NONE) {
// JSON is valid
}
// OR this is equivalent
if (json_last_error() === 0) {
// JSON is valid
}
注意json_last_error仅在PHP >= 5.3.0中支持。
完整的程序来检查准确的错误
在开发期间了解准确的错误总是好的。下面是基于PHP文档检查确切错误的完整程序。
function json_validate($string)
{
// decode the JSON data
$result = json_decode($string);
// switch and check possible JSON errors
switch (json_last_error()) {
case JSON_ERROR_NONE:
$error = ''; // JSON is valid // No error has occurred
break;
case JSON_ERROR_DEPTH:
$error = 'The maximum stack depth has been exceeded.';
break;
case JSON_ERROR_STATE_MISMATCH:
$error = 'Invalid or malformed JSON.';
break;
case JSON_ERROR_CTRL_CHAR:
$error = 'Control character error, possibly incorrectly encoded.';
break;
case JSON_ERROR_SYNTAX:
$error = 'Syntax error, malformed JSON.';
break;
// PHP >= 5.3.3
case JSON_ERROR_UTF8:
$error = 'Malformed UTF-8 characters, possibly incorrectly encoded.';
break;
// PHP >= 5.5.0
case JSON_ERROR_RECURSION:
$error = 'One or more recursive references in the value to be encoded.';
break;
// PHP >= 5.5.0
case JSON_ERROR_INF_OR_NAN:
$error = 'One or more NAN or INF values in the value to be encoded.';
break;
case JSON_ERROR_UNSUPPORTED_TYPE:
$error = 'A value of a type that cannot be encoded was given.';
break;
default:
$error = 'Unknown JSON error occured.';
break;
}
if ($error !== '') {
// throw the Exception or exit // or whatever :)
exit($error);
}
// everything is OK
return $result;
}
使用有效的JSON INPUT进行测试
$json = '[{"user_id":13,"username":"stack"},{"user_id":14,"username":"over"}]';
$output = json_validate($json);
print_r($output);
有效的输出
Array
(
[0] => stdClass Object
(
[user_id] => 13
[username] => stack
)
[1] => stdClass Object
(
[user_id] => 14
[username] => over
)
)
使用无效JSON进行测试
$json = '{background-color:yellow;color:#000;padding:10px;width:650px;}';
$output = json_validate($json);
print_r($output);
无效的输出
Syntax error, malformed JSON.
额外注意(PHP >= 5.2 && PHP < 5.3.0)
由于PHP 5.2中不支持json_last_error,因此可以检查编码或解码是否返回布尔值FALSE。这里有一个例子
// decode the JSON data
$result = json_decode($json);
if ($result === FALSE) {
// JSON is invalid
}
使用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)。
下面是我创建的一个简单的性能函数(在使用json_decode处理更大的字符串之前使用基本的字符串验证):
function isJson($string) {
$response = false;
if (
is_string($string) &&
($string = trim($string)) &&
($stringLength = strlen($string)) &&
(
(
stripos($string, '{') === 0 &&
(stripos($string, '}', -1) + 1) === $stringLength
) ||
(
stripos($string, '[{') === 0 &&
(stripos($string, '}]', -1) + 2) === $stringLength
)
) &&
($decodedString = json_decode($string, true)) &&
is_array($decodedString)
) {
$response = true;
}
return $response;
}
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;
}