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

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

有没有表演爱好者想改进这种方法?


当前回答

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;
}

其他回答

另一种简单的方法

function is_json($str)
{
    return is_array(json_decode($str,true));
}

你真正需要做的就是…

if (is_object(json_decode($MyJSONArray))) 
{ 
    ... do something ...
}

这个请求甚至不需要一个单独的函数。只需将is_object包装在json_decode周围,然后继续。似乎这个解决方案让人们花了太多心思。

为PHP 5.2兼容性新创建的函数,如果你需要成功解码的数据:

function try_json_decode( $json, & $success = null ){
  // non-strings may cause warnings
  if( !is_string( $json )){
    $success = false;
    return $json;
  }

  $data = json_decode( $json );

  // output arg
  $success =

    // non-null data: success!
    $data !==  null  ||

    // null data from 'null' json: success!
    $json === 'null' ||

    // null data from '  null  ' json padded with whitespaces: success!
    preg_match('/^\s*null\s*$/', $json );

  // return decoded or original data
  return $success ? $data : $json;
}

用法:

$json_or_not = ...;

$data = try_json_decode( $json_or_not, $success );

if( $success )
     process_data( $data );
else what_the_hell_is_it( $data );

一些测试:

var_dump( try_json_decode( array(), $success ), $success );
// ret = array(0){}, $success == bool(false)

var_dump( try_json_decode( 123, $success ), $success );
// ret = int(123), $success == bool(false)

var_dump( try_json_decode('      ', $success ), $success );
// ret = string(6) "      ", $success == bool(false)

var_dump( try_json_decode( null, $success ), $success );
// ret = NULL, $success == bool(false)

var_dump( try_json_decode('null', $success ), $success );
// ret = NULL, $success == bool(true)

var_dump( try_json_decode('  null  ', $success ), $success );
// ret = NULL, $success == bool(true)

var_dump( try_json_decode('  true  ', $success ), $success );
// ret = bool(true), $success == bool(true)

var_dump( try_json_decode('  "hello"  ', $success ), $success );
// ret = string(5) "hello", $success == bool(true)

var_dump( try_json_decode('  {"a":123}  ', $success ), $success );
// ret = object(stdClass)#2 (1) { ["a"]=> int(123) }, $success == bool(true)

使用json_decode“探测”它实际上可能不是最快的方法。如果它是一个嵌套很深的结构,那么实例化大量数组对象来丢弃它们是浪费内存和时间。

所以使用preg_match和RFC4627正则表达式来确保有效性可能会更快:

  // in JS:
  var my_JSON_object = !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(
         text.replace(/"(\\.|[^"\\])*"/g, '')));

在PHP中也一样:

  return !preg_match('/[^,:{}\\[\\]0-9.\\-+Eaeflnr-u \\n\\r\\t]/',
       preg_replace('/"(\\.|[^"\\\\])*"/', '', $json_string));

不过,我不是一个性能爱好者,不会在这里费心进行基准测试。

最快的方法是“可能解码”可能的JSON字符串

这真的是最快的方法吗?

如果你想解码复杂的对象或更大的数组,这是最快的解决方案! 除了速度快之外,这是唯一可以可靠地处理任何类型输入值的解决方案——在某些情况下,其他函数会抛出错误或返回不正确的结果。

如果您的JSON字符串包含较短的值(例如,字符串,数字或只有1-2个属性的对象),那么在这个SO问题中的所有解决方案都得到类似的性能。

这里有一个快速的概览和比较-您可以在链接的要点中找到测试用例。最后一列使用了这个答案的代码:

PHP version: 7.4.21

test1: json_last_error() == JSON_ERROR_NONE
test2: is_object( json_decode() )
test3: json_decode() && $res != $string
test4: preg_match()
test5: "maybe decode" approach

      | test1    | test2    | test3    | test4    | test5    
   #0 | 0.0147   | 0.0109 ✓︎ | 0.0119   | 0.0177   | 0.0194   
   #1 | 0.0129   | 0.0106   | 0.0098   | - INV -  | 0.0078 ✓︎ 
   #2 | 0.0076   | 0.0075   | 0.0063 ✓︎ | 0.0083   | 0.0133   
   #3 | 0.0126   | 0.0105   | 0.0096 ✓︎ | - INV -  | 0.0172   
   #4 | 0.0070   | - INV -  | 0.0061 ✓︎ | 0.0141   | 0.0134   
   #5 | 0.0114   | - INV -  | 0.0101   | 0.0075 ✓︎ | 0.0168   
   #6 | 0.0203   | - INV -  | 0.0195   | 0.0073 ✓︎ | 0.0259   
   #7 | 0.0046   | - INV -  | - INV -  | 0.0077   | 0.0031 ✓︎ 
   #8 | 0.0066   | - INV -  | - INV -  | 0.0081   | 0.0020 ✓︎ 
   #9 | 1.0781   | - INV -  | 1.0555   | 0.0998 ✓︎ | 1.0385   
  #10 | 0.3183 ✓︎ | 0.3246   | 0.3270   | 1.0186   | 0.3311   
  #11 | 0.0071   | 0.0068   | 0.0067 ✓︎ | - INV -  | 0.0079   
  #12 | - ERR -  | - ERR -  | - ERR -  | - ERR -  | 0.0025 ✓︎ 
  #13 | - ERR -  | - ERR -  | - ERR -  | - ERR -  | 0.0024 ✓︎ 
  Avg | 0.1251   | 0.0618 ✓︎ | 0.1463   | 0.1321   | 0.1072

请注意,最快的解决方案会产生最不正确的结果。在所有其他解决方案中,“可能解码”方法不仅是最快的,而且是唯一具有正确结果的解决方案。

下面是完整的性能比较脚本,在那里您可以看到我用于比较的测试数据:https://gist.github.com/stracker-phil/6a80e6faedea8dab090b4bf6668ee461


“可能解码”逻辑/代码

在尝试解码JSON字符串之前,我们首先执行一些类型检查和字符串比较。这为我们提供了最佳性能,因为json_decode()可能很慢。

/**
 * Returns true, when the given parameter is a valid JSON string.
 */
function is_json( $value ) {
    // Numeric strings are always valid JSON.
    if ( is_numeric( $value ) ) { return true; }

    // A non-string value can never be a JSON string.
    if ( ! is_string( $value ) ) { return false; }

    // Any non-numeric JSON string must be longer than 2 characters.
    if ( strlen( $value ) < 2 ) { return false; }

    // "null" is valid JSON string.
    if ( 'null' === $value ) { return true; }

    // "true" and "false" are valid JSON strings.
    if ( 'true' === $value ) { return true; }
    if ( 'false' === $value ) { return true; }

    // Any other JSON string has to be wrapped in {}, [] or "".
    if ( '{' != $value[0] && '[' != $value[0] && '"' != $value[0] ) { return false; }

    // Verify that the trailing character matches the first character.
    $last_char = $value[strlen($value) -1];
    if ( '{' == $value[0] && '}' != $last_char ) { return false; }
    if ( '[' == $value[0] && ']' != $last_char ) { return false; }
    if ( '"' == $value[0] && '"' != $last_char ) { return false; }

    // See if the string contents are valid JSON.
    return null !== json_decode( $value );
}

额外:使用此逻辑安全地对JSON进行双重解码

此函数使用相同的逻辑,但返回解码后的JSON对象或原始值。

我在递归解码复杂对象的解析器中使用了这个函数。一些属性可能已经被早期的迭代解码了。该函数识别此值,并且不再尝试再次对值进行双解码。

/**
 * Tests, if the given $value parameter is a JSON string.
 * When it is a valid JSON value, the decoded value is returned.
 * When the value is no JSON value (i.e. it was decoded already), then 
 * the original value is returned.
 */
function get_data( $value, $as_object = false ) {
    if ( is_numeric( $value ) ) { return 0 + $value; }
    if ( ! is_string( $value ) ) { return $value; }
    if ( strlen( $value ) < 2 ) { return $value; }
    if ( 'null' === $value ) { return null; }
    if ( 'true' === $value ) { return true; }
    if ( 'false' === $value ) { return false; }
    if ( '{' != $value[0] && '[' != $value[0] && '"' != $value[0] ) { return $value; }

    $json_data = json_decode( $value, $as_object );
    if ( is_null( $json_data ) ) { return $value; }
    return $json_data;
}

注意:在这个SO问题中,当将一个非字符串传递给任何其他解决方案时,您将得到显著的性能下降+错误的返回值(甚至是致命的错误)。此代码是防弹和高性能的。