434

I need a really, really fast method of checking if a string is JSON or not. I feel like this is not the best way:

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

Any performance enthusiasts out there want to improve this method?

Madan Sapkota
  • 21,350
  • 11
  • 100
  • 111
Kirk Ouimet
  • 23,368
  • 41
  • 108
  • 164
  • 3
    Consider only using `json_decode` once... also, check the input and return values of `json_decode`. –  May 18 '11 at 08:20
  • 6
    So, which one is the answer? – Farid Rn Dec 06 '12 at 19:11
  • 10
    The ternary switch here is redundant. Your statement already evaluates as a boolean. – I wrestled a bear once. Apr 09 '15 at 17:46
  • Possible duplicate of [How to determine whether a string is valid JSON?](http://stackoverflow.com/questions/1187576/how-to-determine-whether-a-string-is-valid-json) – A.L Jan 13 '16 at 21:23
  • Accept the answer of Lewis Donovan ...it is working fine – Poonam Bhatt Sep 04 '18 at 13:24
  • looking at the solutions, **performance vs simplicity** should have been the prime focus of OP because fastest always depends on the use case, for eg., If you know you'll always deal with object/array, checking for first char of the string should suffice. Generalizing a solution can be never be "fastest"; "faster", may be. – Fr0zenFyr Feb 20 '19 at 08:45
  • @Kirk-Ouimet lots of great answers here. Can you accept one to give credit where credit is due? – Jerry Lupo Oct 18 '20 at 04:31
  • Update 2021: Most solution here focus on the **shortest** way, but the OP asked for the **fastest** way. This is the fastest solution I found - compared to all other approaches in this thread: https://stackoverflow.com/a/45241792/313501 – Philipp Apr 09 '21 at 19:15

32 Answers32

640
function isJson($string) {
   json_decode($string);
   return json_last_error() === JSON_ERROR_NONE;
}
Fabien Sa
  • 7,626
  • 2
  • 35
  • 40
Henrik P. Hessel
  • 35,062
  • 17
  • 77
  • 99
  • 21
    Looks like everyone is loving this answer. Any explanation why? – Kirk Ouimet May 18 '11 at 17:39
  • 8
    I believe PHP 5.3 > is needed to use the json_last_error function – Chris Harrison Sep 09 '11 at 02:55
  • 2
    I think it's good because it doesn't rely on heuristics, uses native php functionality, and is about as future-proof as you're gonna get; it just tells you straight up whether there were any errors in decoding the string. No errors => valid JSON. – Jon z Sep 05 '12 at 19:42
  • 108
    Checking first character of string for `{`, `[` or first symbol of any other literal can potentially greatly speed this one up when many of incoming strings are expected to be non-JSON. – Oleg V. Volkov Sep 25 '12 at 17:03
  • The problem with this is empty `$string` values, which will return true even though they are considered invalid JSON. – upful Oct 21 '13 at 15:40
  • 21
    $phone = '021234567'; var_dump(isJson($phone)); return true no! it should return false. – vee Jan 02 '14 at 17:12
  • 24
    Beware, this function will return true for any number also, whether you specify it as a string or a true number. `6.5 = true, '300' = true, 9 = true` etc. So this might be a valid JSON value but the function might not behave as you expect, if you want to check only for valid JSON strings with `{}` or `[]`; – BadHorsie Feb 25 '14 at 16:57
  • 1
    I agree with Oleg, upful vee and BadHorsie, and accepted answer should be mario's answer, because this function does not tell you is it a JSON or not, it tells you just is json_decode probe has errors or not. So this function name should be isJsonDecodeProbe(); – vaso123 Mar 12 '14 at 12:55
  • 1
    Sorry. I'm down-voting this because of reasons mentioned above. Depends on what exactly you require from the function, but if any kind of user-input validity is being done, there is potentially a small loophole. – cartbeforehorse Jul 09 '14 at 07:54
  • 1
    Really best solution. Should be accepted. | http://i.imgur.com/fYYgTKF.png | **Result 01** = _first code_ | **Result 02** = _this answer_ | **x** = _times repeated_ | **y** = _sec. execution_ – M1K1O Jul 31 '14 at 16:32
  • 1
    Surely it just needs `if (is_numeric($string)) return false;` as the first line of the function to catch number only $string values? – Paul Phillips Mar 18 '15 at 09:58
  • terrible answer. For input strings consisting solely of digits, the validation will pass. Surely, a string of digits is not a valid JSON. – radhoo Apr 01 '15 at 16:53
  • 1
    @M1K1O - What are you referencing with 'Result 01' and 'Result 02?' It's not entirely clear what is the best answer... – Erutan409 Apr 06 '15 at 16:58
  • 12
    It's worth noting that this works correctly in theory. Unfortunately PHP's `json_decode` function has a number of bugs, which will allow invalid JSON to be parsed in odd ways. `isJson('0123')` should return `false` because `0123` is not JSON, however `isJson('123')` should return `true` because `123` *is* JSON. It seems that some people are not aware that [JSON allows values to be more than just an object or array](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf). Valid JSON values may be objects, arrays, numbers, strings, booleans, and `null`. – zzzzBov May 08 '15 at 01:30
  • 1
    We obviously use json_decode() if we are really going to parse the data and use it in our code. I such case there is no hamper of speed as you are doing a obvious thing. If there is no need to decode. for example if you are just storing it in DB then you may consider alternative way. – Imdad Sep 24 '15 at 14:13
  • 2
    I believe this answer highlights that in most cases, testing for json-ness is probably unnecessary or even detrimental - just decode everything and handle the errors. If you're conditionally encoding your data based on whether it's scalar or not, you should probably just *encode* everything because json's handling of scalar data is pretty straight forward as it is. If other data types are more common, you should be attempting to deal with those first before you even get to attempting a json decode. – Jason Feb 09 '16 at 16:38
  • 1
    This function is incorrect because, if you send only string value to the function it returns true. It has to validate as is it object as answered by user1653711 below. – Shripad Bhat Mar 16 '16 at 03:07
  • 1
    It will return true if you pass "12345" as a string. – Kostiantyn Oct 11 '17 at 06:42
  • 1
    Sorry. I'm down-voting because string number is passed the check. – Tinh Dang Apr 09 '18 at 09:09
  • A non-zero number will pass this test, so I'm downvoting. You need to `json_decode()` the string and check if it's an object or an array. If it fails this test, then it's not valid JSON. See: https://stackoverflow.com/a/51636727/7546845 – Lewis Donovan Jan 07 '19 at 11:25
  • You might want to catch any `ErrorExceptions` or check if it's not an array or object. I combined this with [Tinh Dang's solution](https://stackoverflow.com/a/49729033/3209381) – sykez Jan 03 '20 at 14:48
  • Just to clear things out. A plain number (int or float), bools, null and strings (with quotes at start and end) are valid json values! This function does exactly, what it should do. Thanks for sharing. – scotty86 Aug 25 '20 at 07:38
  • it returns true if you supply numbers or letters – Vikas Kandari Sep 29 '20 at 12:33
184

Answer to the Question

The function json_last_error returns the last error occurred during the JSON encoding and decoding. So the fastest way to check the valid JSON is

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

Note that json_last_error is supported in PHP >= 5.3.0 only.

Full program to check the exact ERROR

It is always good to know the exact error during the development time. Here is full program to check the exact error based on PHP docs.

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

Testing with Valid JSON INPUT

$json = '[{"user_id":13,"username":"stack"},{"user_id":14,"username":"over"}]';
$output = json_validate($json);
print_r($output);

Valid OUTPUT

Array
(
    [0] => stdClass Object
        (
            [user_id] => 13
            [username] => stack
        )

    [1] => stdClass Object
        (
            [user_id] => 14
            [username] => over
        )
)

Testing with invalid JSON

$json = '{background-color:yellow;color:#000;padding:10px;width:650px;}';
$output = json_validate($json);
print_r($output);

Invalid OUTPUT

Syntax error, malformed JSON.

Extra note for (PHP >= 5.2 && PHP < 5.3.0)

Since json_last_error is not supported in PHP 5.2, you can check if the encoding or decoding returns boolean FALSE. Here is an example

// decode the JSON data
$result = json_decode($json);
if ($result === FALSE) {
    // JSON is invalid
}

Hope this is helpful. Happy Coding!

Madan Sapkota
  • 21,350
  • 11
  • 100
  • 111
  • Little precision : if this json is valid but a previous decoded one is invalid, your code is going to work correctly, because : "*Returns the last error (if any) occurred during the __last__ JSON encoding/decoding.*" – Bruno Dec 20 '16 at 11:58
  • Thanks @Madan, the "json_decode" verification solved to me that I running PHP 7.0. – Francis Rodrigues Feb 25 '17 at 14:30
  • Surely json_decode could just return false for the literal false, so a check `((strlen($json) === 5) && ($json !== 'false'))` should also be undertaken to avoid that edge? – MrMesees Jul 11 '17 at 08:18
  • @Bruno If the last decoding works without errors then `json_last_error` returns `JSON_ERROR_NONE`. – Andrea Feb 14 '18 at 13:50
91

All you really need to do is this...

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

This request does not require a separate function even. Just wrap is_object around json_decode and move on. Seems this solution has people putting way too much thought into it.

ns16
  • 1,014
  • 2
  • 10
  • 19
user1653711
  • 985
  • 6
  • 3
  • 3
    @RomanM.Kos Just to be clear, if the array is a simple array, then you need to use `is_array` in addition to `is_object`, else `is_object` will return false for simple arrays encoded as JSON. So @ggutenberg is right in this case. Passing the true argument to `json_decode` forces an object to be returned as an array. You could in theory always force the decode to an array and just check of `is_array`, that should work. – userabuser Feb 14 '14 at 10:24
  • @userabuser If i `json_encode($array)` for simple PHP array, and then do `json_decode($str)` i will receive object, but not array. `json_decode($str, true)` forces to convert into array. Why do complicated string in your code? Check for `is_array(json_decode($str, true))` and some time later when you read it you will understand that decoded must be only an array. Much harder to guess `is_object(json_decode($MyJSONArray))` "Oh, here i am checking for decoded is an array or not?" – Roman M. Koss Feb 14 '14 at 22:35
  • @RomanM.Kos No, that's not correct, http://codepad.viper-7.com/OFrtsq - as I said, you can always force `json_decode` to return an array to save you checking for object and array, but if you don't AND you `json_decode` what was a simple array to begin with, you will receive an array in return on decode, not an object. You must use `JSON_FORCE_OBJECT` if you want to always force an object on encode IF passing a simple array. – userabuser Feb 15 '14 at 05:07
  • 19
    Downvote for saying: `This request does not require a separate function even`. Strictly speaking, no solution requires a separate function. The point of a function is _not_ to make multiple lines of code look like one line of code. The point of the function is to make the JSON-checking process standard everywhere in your application, so that different programmers (or the same programmer over time) don't use different checking procedures at different stages in the flow of the program. – cartbeforehorse Jul 09 '14 at 08:17
75

Using json_decode to "probe" it might not actually be the fastest way. If it's a deeply nested structure, then instantiating a lot of objects of arrays to just throw them away is a waste of memory and time.

So it might be faster to use preg_match and the RFC4627 regex to also ensure validity:

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

The same in PHP:

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

Not enough of a performance enthusiast to bother with benchmarks here however.

mario
  • 138,064
  • 18
  • 223
  • 277
  • great idea, my understanding is that this only checks for validity (all strings enclosed in strings) and not the correct syntax. – Henrik P. Hessel May 18 '11 at 17:46
  • It's actually more a check for invalidity and against JS exploits. Verifying the structure would require a more elaborate `(?R)` regex, doable, but seemingly nobody attempted that yet. – mario May 18 '11 at 17:53
  • This is, indeed, the best way to try before going though an ugly brute force sequence of failures to determine what it actually is. This also (partly) answered [my question](http://stackoverflow.com/questions/6115222/determine-data-type-from-file-get-contents) – Tim Post May 24 '11 at 20:08
  • 12
    Complete recursive regex to verify JSON here: http://stackoverflow.com/questions/2583472/regex-to-validate-json/3845829#3845829 - But it turns out PHPs `json_decode` is always faster than a PCRE regex. (Though it's not very optimized, no synthetic tests found, and might behave differently in Perl..) – mario May 31 '11 at 22:44
  • This answer is only partly correct. The regex does not check for valid json-strings but rather a string safely being passed into javascript using `eval()` – Gustav Nov 19 '12 at 13:18
  • 3
    @vee Yes, thanks for the note. But let's keep it here [incorrectly], so nobody actually uses that in production. – mario Jan 02 '14 at 21:12
  • @mario After removing the `g`, I get a `Warning: preg_replace(): Compilation failed: missing terminating ] for character class at...` message in PHP. Seems that you may need to swap `[^"\\]` to `[^\\"]` – cartbeforehorse Jul 09 '14 at 00:10
  • 1
    @cartbeforehorse Okay, thanks. I fixed the escaping wholesome for PHPs double quoted string context then. – mario Jul 09 '14 at 00:30
  • @mario Sorry for being dumb. But why does *everything* need to be double-escaped? – cartbeforehorse Jul 09 '14 at 05:49
  • 1
    @cartbeforehorse It doesn't. Mostly decoration. It's just the literal backslash which does indeed require doubly escaping. For `\r` `\n` `\t` it only makes sense so PHP doesn't interpolate them, but let PCRE interpret them (only was required for `/x` mode). The other occurences do not strictly need it; yet still "the backslash escapes itself" in all string PHP contexts. So one could consider it more exact. – mario Jul 09 '14 at 05:59
  • 4
    @mario Okay, I see. So basically, the PHP escapes the backslashes before the reg-exp engine gets to see it. As far as the reg-exp engine is concerned, there are half the number of backslashes in the string as what we humans see. _"Like reg-exp wasn't complicated enough already"_ – cartbeforehorse Jul 09 '14 at 08:26
  • By some reason the first regexp `/"(\\.|[^"\\\\])*"/` doesnt work for utf8 special characters like `\u00e1` **i just change the regexp to** `/"(\\.|[^"])*"/u` and seems to work allright on php 5.5 on windows. – ontananza Sep 11 '15 at 16:41
  • 1
    I was curious to see if this was actually faster than using json_decode to see if a string is valid JSON so I ran some benchmarks. I ran 100,000 passes of a two-dimensional array that had been encoded using json_encode. `PHP version : 5.5.34 Platform : Darwin -------------------------------------- test_json_decode : 5.608 sec. test_regex : 10.428 sec.` – Alex Plumb Jul 19 '16 at 15:54
  • http://sandbox.onlinephpfunctions.com/code/ee4f2bfa6556ce2343bd4cde0f6fd344c6008931 Regex is definitely the faster method – Asheliahut Oct 19 '17 at 18:58
  • @Asheliahut not completely true, the regex is really slow when checking against large non-JSON payloads. `json_decode()` is pretty fast compared to regex when doing check against JSON payloads. However `json_decode()` is super fast against non-JSON compared to the regex method. So I'd say before choosing any of those methods ask yourself the question what type of payload you are most often expecting to check against. – Rens Tillmann Jun 29 '20 at 02:13
49

This will return true if your string represents a json array or object:

function isJson($str) {
    $json = json_decode($str);
    return $json && $str != $json;
}

It rejects json strings that only contains a number, string or boolean, although those strings are technically valid json.

var_dump(isJson('{"a":5}')); // bool(true)
var_dump(isJson('[1,2,3]')); // bool(true)
var_dump(isJson('1')); // bool(false)
var_dump(isJson('1.5')); // bool(false)
var_dump(isJson('true')); // bool(false)
var_dump(isJson('false')); // bool(false)
var_dump(isJson('null')); // bool(false)
var_dump(isJson('hello')); // bool(false)
var_dump(isJson('')); // bool(false)

It is the shortest way I can come up with.

Cyril
  • 2,430
  • 3
  • 22
  • 25
  • Rather than var_dump, you could put this in a PHPUnit test-case. Otherwise I'm both surprised and happy to learn it's true. – MrMesees Jul 11 '17 at 08:12
  • 4
    Why does everyone else have such long winded answers when this works great? Thanks. – toddmo May 13 '18 at 17:19
  • 2
    Simply, lovely! Didn't check for "fastest way" or performance wise but this one sure covers all the cases I'd ever check for. This is a classic example for the infamous proverb **"Don't use sledgehammer to crack a nut"**. From a programmer point of view, it is always better to keep the code simple, short and easy to understand, performance vs simplicity is another debate out of scope for this thread. – Fr0zenFyr Feb 20 '19 at 08:39
  • This is a neat bit of logic, but also note that it returns false for empty arrays. For example: `var_dump(isJson('[]')); // bool(false)`. According to the documentation on booleans, this is because PHP evaluates arrays with zero elements as false. Here is a minor amendment to adjust the return statement; it performs an identical comparison on the decoded output which handles this case: `return $json !== false && $str != $json;` – j13k Oct 11 '19 at 06:42
  • 1
    @j13k Identical comparison evaluates `isJson('hello')` to true, which isn’t valid json. Loose comparison is chosen on purpose here. I don’t have a quick solution for the empty array/object situation, except an ugly `return $json == '[]' || ...` – Cyril Oct 11 '19 at 10:43
  • @Cyril, apologies—I missed that case because my test function includes additional checks to short-circuit the function before calling `json_decode`. Because the 'hello' string triggers a JSON error, the output of the function is `NULL`, so an additional `!is_null` check is enough to yield the correct results with your test data: `return $json !== false && !is_null($json) && $str != $json;` – j13k Oct 13 '19 at 23:26
  • This is the answer that everyone need, should be the accepted answer instead. – TirtaDev Jul 26 '20 at 13:48
25

The simplest and fastest way that I use is following;

$json_array = json_decode( $raw_json , true );

if( $json_array == NULL )   //check if it was invalid json string
    die ('Invalid');  // Invalid JSON error

 // you can execute some else condition over here in case of valid JSON

It is because json_decode() returns NULL if the entered string is not json or invalid json.


Simple function to validate JSON

If you have to validate your JSON in multiple places, you can always use the following function.

function is_valid_json( $raw_json ){
    return ( json_decode( $raw_json , true ) == NULL ) ? false : true ; // Yes! thats it.
}

In the above function, you will get true in return if it is a valid JSON.

Community
  • 1
  • 1
  • 3
    `json_decode('null') == NULL` and `null` is a valid JSON value. – zzzzBov May 08 '15 at 01:34
  • I have tested if 'null' is valid json at [json.parser.online](http://json.parser.online.fr/) but it seems that its not valid json. And [json_decode()](http://php.net/manual/en/function.json-decode.php) is php core function to validate json so I doubt to get some false result in our output. – Mohammad Mursaleen May 24 '15 at 02:01
  • 1
    Rather than trust some unverified website, [consider consulting the spec, which disagrees (pg 2)](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf). Alternatively, try `JSON.parse('null')` in your dev console. – zzzzBov May 24 '15 at 02:16
22
function is_json($str){ 
    return json_decode($str) != null;
}

http://tr.php.net/manual/en/function.json-decode.php return value is null when invalid encoding detected.

AhmetB - Google
  • 35,086
  • 32
  • 117
  • 191
  • 4
    It will also improperly return null for "null" (which isn't valid JSON, but may be entirely "valid" to json_decode otherwise). Go figure. –  May 18 '11 at 08:22
  • I think this sould be: `json_decode($str)!=null;` or otherwise the function should be called `is_not_json`. – Yoshi May 18 '11 at 08:23
  • That function would be better renamed "is something other than JSON"! – lonesomeday May 18 '11 at 08:23
  • 2
    @user166390, [`json_decode('null')` is valid JSON according to the spec](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf), and should return the value of `null`. – zzzzBov May 08 '15 at 01:23
  • Please also note that with this method `is_json('false')` and `is_json('[]')` will return `false` as type is not checked. I think this method should rather return `$str === null || json_decode($str) !== null`. – Antoine Pinsard Aug 13 '15 at 14:01
12

You must validate your input to make sure the string you pass is not empty and is, in fact, a string. An empty string is not valid JSON.

function is_json($string) {
  return !empty($string) && is_string($string) && is_array(json_decode($string, true)) && json_last_error() == 0;
}

I think in PHP it's more important to determine if the JSON object even has data, because to use the data you will need to call json_encode() or json_decode(). I suggest denying empty JSON objects so you aren't unnecessarily running encodes and decodes on empty data.

function has_json_data($string) {
  $array = json_decode($string, true);
  return !empty($string) && is_string($string) && is_array($array) && !empty($array) && json_last_error() == 0;
}
upful
  • 740
  • 8
  • 25
12

This will do it:

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

As shown in other answers, json_last_error() returns any error from our last json_decode(). However there are some edge use cases where this function alone is not comprehensive enough. For example, if you json_decode() an integer (eg: 123), or a string of numbers with no spaces or other characters (eg: "123"), the json_last_error() function will not catch an error.

To combat this, I've added an extra step that ensures the result of our json_decode() is either an object or an array. If it's not, then we return false.

To see this in action, check these two examples:

Lewis Donovan
  • 403
  • 4
  • 12
  • `"hello"` is a valid JSON, and it's not an object neither an array, `json_last_error()` is enough – JoniJnm Aug 13 '19 at 08:59
  • 1
    `json_last_error()` returns error code `4` when you `json_decode()` the string `"hello"`. Example here: https://3v4l.org/lSsEo – Lewis Donovan Aug 15 '19 at 12:14
  • Your code is wrong, `hello` is not a valid JSON, but `"hello"` is https://3v4l.org/OEJrQ – JoniJnm Sep 06 '19 at 11:46
12

I found this question after coming across something similar in my work, yesterday. My solution in the end was a hybrid of some of the approaches above:

function is_JSON($string) {

  return (is_null(json_decode($string))) ? FALSE : TRUE;

}
Rounin
  • 21,349
  • 4
  • 53
  • 69
  • 1
    I wasn't used too as well, hehe. Since PhpStorm and Magento Code Sniffer tool I was using always complained with me, I started adopting this approach. In the end we get cleaner code and get used to it. :P – Ricardo Martins Jun 24 '20 at 22:54
10

Easy method is to check the json result..

$result = @json_decode($json,true);
    if (is_array($result)) {
        echo 'JSON is valid';
    }else{
        echo 'JSON is not valid';
    }
Rameez Rami
  • 4,039
  • 1
  • 22
  • 30
10

Using PHPBench with the following class, the below results were achieved:

<?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 |
+---------------+-----------------------+-----+------+-----+------------+---------+---------+---------+---------+---------+--------+-------+

Conclusion: The fastest way to check if json is valid is to return json_decode($json, true) !== null).

6

in 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;
}
Parsa
  • 485
  • 5
  • 11
5

Earlier i was just checking for a null value, which was wrong actually.

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

The above piece of code works fine with strings. However as soon as i provide number, it breaks up.for example.

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

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

To fix it what i did was very simple.

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

    if(($r_data != $data) && $r_data)
        print "Json success";
    else
        print "Json error";
Ahad Ali
  • 380
  • 3
  • 10
5
//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;
}
4

Another simple way

function is_json($str)
{
    return is_array(json_decode($str,true));
}
h0mayun
  • 3,259
  • 26
  • 39
  • 1
    This isn't correct. Any PHP type can be encoded into JSON such as objects, strings, etc and the json_decode function is expected to return them. This is only true if you are always decoding arrays and no other variable types. – Chaoix Aug 15 '14 at 19:38
  • @Chaoix using `json_decode($str,true)` makes it convert objects to arrays so it will pass the is_array check. You correct about strings, integers, etc. though. – Paul Phillips Mar 18 '15 at 10:05
  • I see the what you mean about the second parameter on json_encode. I still think @Ahad Ali's solution is a much better one in terms of typing and only doing a json_decode once in your algorithms. – Chaoix Mar 26 '15 at 17:08
4

We need to check if passed string is not numeric because in this case json_decode raises no error.

function isJson($str) {
    $result = false;
    if (!preg_match("/^\d+$/", trim($str))) {
        json_decode($str);
        $result = (json_last_error() == JSON_ERROR_NONE);
    }

    return $result;
}
Sergey Onishchenko
  • 4,740
  • 2
  • 34
  • 43
3

I've tried some of those solution but nothing was working for me. I try this simple thing :

$isJson = json_decode($myJSON);

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

I think it's a fine solutiuon since JSON decode without the second parameter give an object.

EDIT : If you know what will be the input, you can adapt this code to your needs. In my case I know I have a Json wich begin by "{", so i don't need to check if it's an array.

Greco Jonathan
  • 2,337
  • 2
  • 24
  • 49
  • Your JSON could potentially just be an array, in which case it would be an array rather than instead of 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 Feb 13 '18 at 21:48
3

Should be something like this:

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

UnitTest

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);
    }
}
Tinh Dang
  • 400
  • 2
  • 10
  • I like that you're checking if it's a string. Goes well in combination with the first solution to avoid `ErrorException` if the string is array or object. – sykez Jan 03 '20 at 14:45
2

I don't know about performance or elegance of my solution, but it's what I'm using:

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

Since all my JSON encoded strings start with {" it suffices to test for this with a RegEx. I'm not at all fluent with RegEx, so there might be a better way to do this. Also: strpos() might be quicker.

Just trying to give in my tuppence worth.

P.S. Just updated the RegEx string to /^[\[\{]\"/ to also find JSON array strings. So it now looks for either [" or {" at the beginning of the string.

maxpower9000
  • 225
  • 2
  • 8
2

Hi here's a little snippet from my library, in this first condition I'm just checking if the data is json then return it if correctly decoded, please note the substr usage for performance ( I haven't seen yet any json file not begining neither by { or [

$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
    }
}
Jack
  • 152
  • 1
  • 8
  • There have been 34 answers posted to this question, many of which also subscribe to the (mistaken) belief that JSON has to represent an array or an object. Is this answer doing anything different from the other 3 dozen answers? – miken32 Dec 05 '18 at 15:57
2

Another suggestion from me :)

function isJson(string $string) {
  return ($result = json_decode($string, true)) ? $result : $string;
}
kalicki2k
  • 376
  • 2
  • 6
1

Expanding on this answer How about the following:

<?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);
?>
Community
  • 1
  • 1
Robert Johnstone
  • 4,937
  • 9
  • 48
  • 84
  • 1
    Shouldn't the substring check be made before executing the decode to save time if the error is found in that check? I would imagine that 4 substring checks would be faster than a json_decode, but if someone could back me up with this assumption I'd appreciate any thoughts on this. – Mark Aug 07 '15 at 15:04
  • That's a fare argument. I don't know the processing time involved, but if it's faster then yes. – Robert Johnstone Aug 17 '15 at 11:11
1

The fastest way is to "maybe decode" the possible JSON string

Is this really the fastest method?

If you want to decode complex objects or larger arrays, this is the fastest way, by far!

If your json string contains short values (like scalars or objects with only 1-2 attributes) then all solutions in this SO questions come to a similar performance.

Here is a performance comparison that I've done with some dummy and real-live JSON values:

PHP version: 7.4.12
1 | Duration: 1.5828 sec | 60,000 calls | json_last_error() == JSON_ERROR_NONE
2 | Duration: 1.5202 sec | 60,000 calls | is_object( json_decode() )
3 | Duration: 1.5400 sec | 60,000 calls | json_decode() && $res != $string
4 | Duration: 1.3536 sec | 60,000 calls | preg_match()
5 | Duration: 0.2261 sec | 60,000 calls | "maybe decode" approach

The last line is the code from this answer, which is the "maybe decode" approach.

Here is the performance comparison script, there you can see the test-data I used for the comparison: https://gist.github.com/stracker-phil/6a80e6faedea8dab090b4bf6668ee461


The "maybe decode" logic/code

We first perform some type checks and string comparisons before attempting to decode the JSON string. This gives us the best performance because json_decode() can be slow.

/**
 * Returns true, when the given parameter is a valid JSON string.
 */
function is_json( $value ) {
    // A non-string value can never be a JSON string.
    if ( ! is_string( $value ) ) { return false; }

    // Numeric strings are always valid JSON.
    if ( is_numeric( $value ) ) { return true; }

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

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

    // Note the last param (1), this limits the depth to the first level.
    $json_data = json_decode( $value, null, 1 );

    // When json_decode fails, it returns NULL.
    if ( is_null( $json_data ) ) { return false; }
    return true;
}

Extra: Use this logic to safely double-decode JSON

This function uses the same logic but either returns the decoded JSON object or the original value.

I use this function in a parser that recursively decodes a complex object. Some attributes might be decoded already by an earlier iteration. That function recognizes this and does not attempt to double decode the value again.

/**
 * 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_string( $value ) ) { return $value; }
    if ( is_numeric( $value ) ) { return 0 + $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;
}

Note: When passing a non-string to any other solution in this SO question, you will get dramatically degraded performance + wrong return values (or even fatal errors). This code is bulletproof and highly performant.

Philipp
  • 6,660
  • 5
  • 45
  • 54
  • I'm not sure, why this answer is downvoted and even has a delete request. My performance test clearly shows that it's the fastest method by far. Here is the performance comparison script: https://gist.github.com/stracker-phil/6a80e6faedea8dab090b4bf6668ee461 – Philipp Feb 06 '21 at 14:52
0
$r = (array)json_decode($arr);
if(!is_array($r) || count($r) < 1) return false;
  • 5
    There are already thirty other answers to this question, including one with more than 600 upvotes. That doesn't mean a new answer can't contribute something new to the conversation. But it does mean that your answer would really benefit from additional explanation. What sets it apart from the other answers? Why or when might someone prefer your approach? Has something changed in the language that enables your approach or invalidates previous approaches? Please edit your answer to help differentiate your approach from the other thirty answers that have been contributed over the last decade. – Jeremy Caney Dec 01 '20 at 22:17
  • In the current version of PHP is_array will always return a false, so it should be declared as an array and then check if it has values. and in addition it's a shorter way – איש נחמד Dec 02 '20 at 11:28
0

If a local file stations.json is invalid, missing, or is more than one month old, do something.

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
}
NVRM
  • 6,477
  • 1
  • 51
  • 60
-1

The custom function

function custom_json_decode(&$contents=NULL, $normalize_contents=true, $force_array=true){

    //---------------decode contents---------------------

    $decoded_contents=NULL;

    if(is_string($contents)){

        $decoded_contents=json_decode($contents,$force_array);

    }

    //---------------normalize contents---------------------

    if($normalize_contents===true){

        if(is_string($decoded_contents)){

            if($decoded_contents==='NULL'||$decoded_contents==='null'){

                $contents=NULL;
            }
            elseif($decoded_contents==='FALSE'||$decoded_contents==='false'){

                $contents=false;
            }
        }
        elseif(!is_null($decoded_contents)){

            $contents=$decoded_contents;
        }
    }
    else{

        //---------------validation contents---------------------

        $contents=$decoded_contents;
    }

    return $contents;
}

Cases

$none_json_str='hello';

//------------decoding a none json str---------------

$contents=custom_json_decode($none_json_str); // returns 'hello'

//------------checking a none json str---------------

custom_json_decode($none_json_str,false);

$valid_json=false;

if(!is_null($none_json_str)){

    $valid_json=true;

}

Resources

https://gist.github.com/rafasashi/93d06bae83cc1a1f440b

RafaSashi
  • 14,170
  • 8
  • 71
  • 85
-1

Freshly-made function for PHP 5.2 compatibility, if you need the decoded data on success:

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

Usage:

$json_or_not = ...;

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

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

Some tests:

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)
biziclop
  • 13,654
  • 3
  • 45
  • 62
-1
function is_json($input) {

    $input = trim($input);

    if (substr($input,0,1)!='{' OR substr($input,-1,1)!='}')
        return false;

    return is_array(@json_decode($input, true));
}
GONZO
  • 37
  • 1
  • 4
-1

A simple modification to henrik's answer to touch most required possibilities.

( including " {} and [] " )

function isValidJson($string) {
    json_decode($string);
    if(json_last_error() == JSON_ERROR_NONE) {

        if( $string[0] == "{" || $string[0] == "[" ) { 
            $first = $string [0];

            if( substr($string, -1) == "}" || substr($string, -1) == "]" ) {
                $last = substr($string, -1);

                if($first == "{" && $last == "}"){
                    return true;
                }

                if($first == "[" && $last == "]"){
                    return true;
                }

                return false;

            }
            return false;
        }

        return false;
    }

    return false;

}
-1

Here's a performant and simple function I created (which uses basic string validation before using json_decode for larger strings):

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

This is what I recommend

if (!in_array(substr($string, 0, 1), ['{', '[']) || !in_array(substr($string, -1), ['}', ']'])) {
  return false;
} else {
  json_decode($string);
  return (json_last_error() === JSON_ERROR_NONE);
}
user10012
  • 193
  • 1
  • 2
  • 11