64

I'm trying handle bad json data when parsed through json_decode(). I'm using the following script:

if(!json_decode($_POST)) {
  echo "bad json data!";
  exit;
}

If $_POST equals:

'{ bar: "baz" }'

Then json_decode handles the error fine and spits out "bad json data!"; However, if I set $_POST to something like "invalid data", it gives me:

Warning: json_decode() expects parameter 1 to be string, array given in C:\server\www\myserver.dev\public_html\rivrUI\public_home\index.php  on line 6
bad json data!

Do I need to write a custom script to detect valid json data, or is there some other nifty way to detect this?

2
  • $_POST is always an array containing the x-www-form-urlencoded parameters passed via POST. How do you send your data to your PHP script? Commented Feb 27, 2010 at 16:58
  • The included json functions in PHP are not much help. They have a lot of issues. Take a look at json.org to find a good library. Commented Feb 27, 2010 at 17:02

8 Answers 8

131

Here are a couple of things about json_decode :

  • it returns the data, or null when there is an error
  • it can also return null when there is no error : when the JSON string contains null
  • it raises a warning where there is a warning -- warning that you want to make disappear.

To solve the warning problem, a solution would be to use the [`@` operator][2] *(I don't often recommend using it, as it makes debuging a lot more harder... But here, there is not much of a choice)* :
$_POST = array(
    'bad data'
);
$data = @json_decode($_POST);

You'd then have to test if $data is null -- and, to avoid the case in which json_decode returns null for null in the JSON string, you could check json_last_error, which (quoting) :

Returns the last error (if any) occurred by last JSON parsing.


Which means you'd have to use some code like the following :
if ($data === null
    && json_last_error() !== JSON_ERROR_NONE) {
    echo "incorrect data";
}
Sign up to request clarification or add additional context in comments.

2 Comments

As Harry Bosch pointed out in another answer, you need only check for JSON_ERROR_NONE !== json_last_error()
PHP 8 returns the error if a function fails with "@" (instead of returning false as before).
48

Since PHP 7.3, the json_decode function will accept a new JSON_THROW_ON_ERROR option that will let json_decode throw an exception instead of returning null on error.


Example:

try {  
  json_decode("{", false, 512, JSON_THROW_ON_ERROR);  
}  
catch (\JsonException $exception) {  
  echo $exception->getMessage(); // displays "Syntax error"  
}

4 Comments

This should be the accepted answer. JSON_THROW_ON_ERROR constant
I received an exception: "Warning: Use of undefined constant JSON_THROW_ON_ERROR - assumed 'JSON_THROW_ON_ERROR' (this will throw an Error in a future version of PHP) in <file_path>
@domdambrogia The JSON_THROW_ON_ERROR constant is only available from PHP 7.3
doesn't work on 7.3.24 the error is there but couldn't catch it
28

You can also use json_last_error : http://php.net/manual/en/function.json-last-error.php

which as documentation says :

Returns the last error (if any) occurred during the last JSON encoding/decoding.

here is an example

json_decode($string);

switch (json_last_error()) {
    case JSON_ERROR_NONE:
        echo ' - No errors';
    break;
    case JSON_ERROR_DEPTH:
        echo ' - Maximum stack depth exceeded';
    break;
    case JSON_ERROR_STATE_MISMATCH:
        echo ' - Underflow or the modes mismatch';
    break;
    case JSON_ERROR_CTRL_CHAR:
        echo ' - Unexpected control character found';
    break;
    case JSON_ERROR_SYNTAX:
        echo ' - Syntax error, malformed JSON';
    break;
    case JSON_ERROR_UTF8:
        echo ' - Malformed UTF-8 characters, possibly incorrectly encoded';
    break;
    default:
        echo ' - Unknown error';
    break;
}

2 Comments

This is completely unnecessary in PHP 5.5 with php.net/manual/en/function.json-last-error-msg.php
You could do something like this with json_last_error_msg: if (JSON_ERROR_NONE !== json_last_error()) throw new Exception(json_last_error_msg());
12

This is how Guzzle handles json

    /**
 * Parse the JSON response body and return an array
 *
 * @return array|string|int|bool|float
 * @throws RuntimeException if the response body is not in JSON format
 */
public function json()
{
    $data = json_decode((string) $this->body, true);
    if (JSON_ERROR_NONE !== json_last_error()) {
        throw new RuntimeException('Unable to parse response body into JSON: ' . json_last_error());
    }

    return $data === null ? array() : $data;
}

Comments

11

I just broke my head over a json syntax error in what appeared to be perfect json: {"test1":"car", "test2":"auto"} from a url encoded string.

But in my case some of the above was html encoded, as adding html_entity_decode($string) did the trick.

$ft = json_decode(html_entity_decode(urldecode(filter_input(INPUT_GET, 'ft', FILTER_SANITIZE_STRING))));

Hopefully this will save someone else some time.

Comments

3

I just spent an hour going through all the possible solutions on this page. I took the liberty of collective all these possible solutions into one function, to make it quicker and easier to try and debug.

I hope it can be of use to someone else.

<?php
/**
 * Decontaminate text
 *
 * Primary sources:
 *  - https://stackoverflow.com/questions/17219916/json-decode-returns-json-error-syntax-but-online-formatter-says-the-json-is-ok
 *  - https://stackoverflow.com/questions/2348152/detect-bad-json-data-in-php-json-decode
 */
function decontaminate_text(
  $text,
  $remove_tags = true,
  $remove_line_breaks = true,
  $remove_BOM = true,
  $ensure_utf8_encoding = true,
  $ensure_quotes_are_properly_displayed = true,
  $decode_html_entities = true
){

  if ( '' != $text && is_string( $text ) ) {
    $text = preg_replace( '@<(script|style)[^>]*?>.*?</\\1>@si', '', $text );
    $text = str_replace(']]>', ']]&gt;', $text);

    if( $remove_tags ){
      // Which tags to allow (none!)
      // $text = strip_tags($text, '<p>,<strong>,<span>,<a>');
      $text = strip_tags($text, '');
    }

    if( $remove_line_breaks ){
      $text = preg_replace('/[\r\n\t ]+/', ' ', $text);
      $text = trim( $text );
    }

    if( $remove_BOM ){
      // Source: https://stackoverflow.com/a/31594983/1766219
      if( 0 === strpos( bin2hex( $text ), 'efbbbf' ) ){
        $text = substr( $text, 3 );
      }
    }

    if( $ensure_utf8_encoding ){

      // Check if UTF8-encoding
      if( utf8_encode( utf8_decode( $text ) ) != $text ){
        $text = mb_convert_encoding( $text, 'utf-8', 'utf-8' );
      }
    }

    if( $ensure_quotes_are_properly_displayed ){
      $text = str_replace('&quot;', '"', $text);
    }

    if( $decode_html_entities ){
      $text = html_entity_decode( $text );
    }

    /**
     * Other things to try
     * - the chr-function: https://stackoverflow.com/a/20845642/1766219
     * - stripslashes (THIS ONE BROKE MY JSON DECODING, AFTER IT STARTED WORKING, THOUGH): https://stackoverflow.com/a/28540745/1766219
     * - This (improved?) JSON-decoder didn't help me, but it sure looks fancy: https://stackoverflow.com/a/43694325/1766219
     */

  }
  return $text;
}

// Example use
$text = decontaminate_text( $text );

// $text = decontaminate_text( $text, false ); // Debug attempt 1
// $text = decontaminate_text( $text, false, false ); // Debug attempt 2
// $text = decontaminate_text( $text, false, false, false ); // Debug attempt 3

$decoded_text = json_decode( $text, true );
echo json_last_error_msg() . ' - ' . json_last_error();
?>

I'll maintain it here: https://github.com/zethodderskov/decontaminate-text-in-php/blob/master/decontaminate-text-preparing-it-for-json-decode.php

Comments

2
/**
 * 
 * custom json_decode 
 * handle json_decode errors
 * 
 * @param type $json_text
 * @return type
 */
public static function custom_json_decode($json_text) {

    $decoded_array = json_decode($json_text, TRUE);
    switch (json_last_error()) {
        case JSON_ERROR_NONE:
            return array(
                "status" => 0,
                "value" => $decoded_array
            );


        case JSON_ERROR_DEPTH:
            return array(
                "status" => 1,
                "value" => 'Maximum stack depth exceeded'
            );

        case JSON_ERROR_STATE_MISMATCH:
            return array(
                "status" => 1,
                "value" => 'Underflow or the modes mismatch'
            );

        case JSON_ERROR_CTRL_CHAR:
            return array(
                "status" => 1,
                "value" => 'Unexpected control character found'
            );

        case JSON_ERROR_SYNTAX:
            return array(
                "status" => 1,
                "value" => 'Syntax error, malformed JSON'
            );

        case JSON_ERROR_UTF8:
            return array(
                "status" => 1,
                "value" => 'Malformed UTF-8 characters, possibly incorrectly encoded'
            );

        default:
            return array(
                "status" => 1,
                "value" => 'Unknown error'
            );
    }
}

Comments

-1

For PHP 5.2+ . Note the first check.

<?php

class JsonUtil {
    static function decode($json, $associative = null, $depth = null) {
        if (strtolower(trim($json)) === "null") {
            return null;
        }
        
        if (is_null($associative)) {
            $res = json_decode($json);
        }
        else {
            if (is_null($depth)) {
                $depth = 512;
            }
            
            $res = json_decode($json, $associative, $depth);
        }
        
        if (is_null($res)) {
            throw new Exception("Unable to decode JSON");
        }
        
        return $res;
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.