Skip to content

Commit

Permalink
[Serializer] Add support for seld/jsonlint in order to enhance error …
Browse files Browse the repository at this point in the history
…messages
  • Loading branch information
ostrolucky committed Aug 1, 2023
1 parent 489c887 commit c61a43d
Show file tree
Hide file tree
Showing 5 changed files with 34 additions and 7 deletions.
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@
"predis/predis": "^1.1|^2.0",
"psr/http-client": "^1.0",
"psr/simple-cache": "^1.0|^2.0|^3.0",
"seld/jsonlint": "^1.10",
"symfony/mercure-bundle": "^0.3",
"symfony/phpunit-bridge": "^5.4|^6.0|^7.0",
"symfony/runtime": "self.version",
Expand Down
1 change: 1 addition & 0 deletions src/Symfony/Component/Serializer/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ CHANGELOG
* Deprecate Doctrine annotations support in favor of native attributes
* Deprecate passing an annotation reader to the constructor of `AnnotationLoader`
* Allow the `Groups` attribute/annotation on classes
* JsonDecode: Add `json_decode_detailed_errors` option

6.3
---
Expand Down
27 changes: 24 additions & 3 deletions src/Symfony/Component/Serializer/Encoder/JsonDecode.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@

namespace Symfony\Component\Serializer\Encoder;

use Seld\JsonLint\JsonParser;
use Symfony\Component\Serializer\Exception\NotEncodableValueException;
use Symfony\Component\Serializer\Exception\UnsupportedException;

/**
* Decodes JSON data.
Expand All @@ -30,6 +32,11 @@ class JsonDecode implements DecoderInterface
*/
public const ASSOCIATIVE = 'json_decode_associative';

/**
* True to enable seld/jsonlint as a source for more specific error messages when json_decode fails.
*/
public const DETAILED_ERROR_MESSAGES = 'json_decode_detailed_errors';

public const OPTIONS = 'json_decode_options';

/**
Expand All @@ -39,6 +46,7 @@ class JsonDecode implements DecoderInterface

private array $defaultContext = [
self::ASSOCIATIVE => false,
self::DETAILED_ERROR_MESSAGES => false,
self::OPTIONS => 0,
self::RECURSION_DEPTH => 512,
];
Expand Down Expand Up @@ -69,6 +77,10 @@ public function __construct(array $defaultContext = [])
* json_decode_options: integer
* Specifies additional options as per documentation for json_decode
*
* json_decode_detailed_errors: bool
* If true, enables seld/jsonlint as a source for more specific error messages when json_decode fails.
* If false or not specified, this method will use default error messages from PHP's json_decode
*
* @throws NotEncodableValueException
*
* @see https://php.net/json_decode
Expand All @@ -89,11 +101,20 @@ public function decode(string $data, string $format, array $context = []): mixed
return $decodedData;
}

if (\JSON_ERROR_NONE !== json_last_error()) {
throw new NotEncodableValueException(json_last_error_msg());
if (\JSON_ERROR_NONE === json_last_error()) {
return $decodedData;
}
$errorMessage = json_last_error_msg();

if (!($context[self::DETAILED_ERROR_MESSAGES] ?? $this->defaultContext[self::DETAILED_ERROR_MESSAGES])) {
throw new NotEncodableValueException($errorMessage);
}

if (!class_exists(JsonParser::class)) {
throw new UnsupportedException(sprintf('Enabling "%s" serializer option requires seld/jsonlint. Try running "composer require seld/jsonlint".', self::DETAILED_ERROR_MESSAGES));
}

return $decodedData;
throw new NotEncodableValueException((new JsonParser())->lint($data)?->getMessage() ?: $errorMessage);
}

public function supportsDecoding(string $format): bool
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,17 +58,20 @@ public static function decodeProvider()
/**
* @dataProvider decodeProviderException
*/
public function testDecodeWithException($value)
public function testDecodeWithException(string $value, string $expectedExceptionMessage, array $context)
{
$this->expectException(UnexpectedValueException::class);
$this->decode->decode($value, JsonEncoder::FORMAT);
$this->expectExceptionMessage($expectedExceptionMessage);
$this->decode->decode($value, JsonEncoder::FORMAT, $context);
}

public static function decodeProviderException()
{
return [
["{'foo': 'bar'}"],
['kaboom!'],
["{'foo': 'bar'}", 'Syntax error', []],
["{'foo': 'bar'}", 'single quotes instead of double quotes', ['json_decode_detailed_errors' => true]],
['kaboom!', 'Syntax error', ['json_decode_detailed_errors' => false]],
['kaboom!', "Expected one of: 'STRING', 'NUMBER', 'NULL',", ['json_decode_detailed_errors' => true]],
];
}
}
1 change: 1 addition & 0 deletions src/Symfony/Component/Serializer/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
},
"require-dev": {
"doctrine/annotations": "^1.12|^2",
"seld/jsonlint": "^1.10",
"phpdocumentor/reflection-docblock": "^3.2|^4.0|^5.0",
"symfony/cache": "^5.4|^6.0|^7.0",
"symfony/config": "^5.4|^6.0|^7.0",
Expand Down

0 comments on commit c61a43d

Please sign in to comment.