-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Serializer] Add options to JsonDecode and JsonEncode to wrap/unwrap json data #30894
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
…cess any node
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,6 +11,7 @@ | |
|
||
namespace Symfony\Component\Serializer\Encoder; | ||
|
||
use Symfony\Component\PropertyAccess\PropertyAccess; | ||
use Symfony\Component\Serializer\Exception\NotEncodableValueException; | ||
|
||
/** | ||
|
@@ -38,7 +39,9 @@ class JsonDecode implements DecoderInterface | |
self::ASSOCIATIVE => false, | ||
self::OPTIONS => 0, | ||
self::RECURSION_DEPTH => 512, | ||
JsonEncoder::JSON_PROPERTY_PATH => null, | ||
]; | ||
private $propertyAccessor; | ||
|
||
/** | ||
* Constructs a new JsonDecode instance. | ||
|
@@ -57,14 +60,15 @@ public function __construct($defaultContext = [], int $depth = 512) | |
} | ||
|
||
$this->defaultContext = array_merge($this->defaultContext, $defaultContext); | ||
$this->propertyAccessor = PropertyAccess::createPropertyAccessor(); | ||
} | ||
|
||
/** | ||
* Decodes data. | ||
* | ||
* @param string $data The encoded JSON string to decode | ||
* @param string $format Must be set to JsonEncoder::FORMAT | ||
* @param array $context An optional set of options for the JSON decoder; see below | ||
* @param array $context an optional set of options for the JSON decoder; see below | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should be reverted |
||
* | ||
* The $context array is a simple key=>value array, with the following supported keys: | ||
* | ||
|
@@ -80,6 +84,9 @@ public function __construct($defaultContext = [], int $depth = 512) | |
* json_decode_options: integer | ||
* Specifies additional options as per documentation for json_decode | ||
* | ||
* JSON_PROPERTY_PATH: string | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. lower case: that's the value of the constant, not its name |
||
* Specifies property path and allow to unwrap data | ||
* | ||
* @return mixed | ||
* | ||
* @throws NotEncodableValueException | ||
|
@@ -91,13 +98,21 @@ public function decode($data, $format, array $context = []) | |
$associative = $context[self::ASSOCIATIVE] ?? $this->defaultContext[self::ASSOCIATIVE]; | ||
$recursionDepth = $context[self::RECURSION_DEPTH] ?? $this->defaultContext[self::RECURSION_DEPTH]; | ||
$options = $context[self::OPTIONS] ?? $this->defaultContext[self::OPTIONS]; | ||
|
||
$propertyPath = $context[JsonEncoder::JSON_PROPERTY_PATH] ?? $this->defaultContext[JsonEncoder::JSON_PROPERTY_PATH]; | ||
$decodedData = json_decode($data, $associative, $recursionDepth, $options); | ||
|
||
if (JSON_ERROR_NONE !== json_last_error()) { | ||
throw new NotEncodableValueException(json_last_error_msg()); | ||
} | ||
|
||
if ($propertyPath) { | ||
if ($this->propertyAccessor->isReadable($decodedData, $propertyPath)) { | ||
$decodedData = $this->propertyAccessor->getValue($decodedData, $propertyPath); | ||
} else { | ||
$decodedData = null; | ||
} | ||
} | ||
|
||
return $decodedData; | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,6 +11,7 @@ | |
|
||
namespace Symfony\Component\Serializer\Encoder; | ||
|
||
use Symfony\Component\PropertyAccess\PropertyAccess; | ||
use Symfony\Component\Serializer\Exception\NotEncodableValueException; | ||
|
||
/** | ||
|
@@ -24,13 +25,16 @@ class JsonEncode implements EncoderInterface | |
|
||
private $defaultContext = [ | ||
self::OPTIONS => 0, | ||
JsonEncoder::JSON_PROPERTY_PATH => null, | ||
]; | ||
private $propertyAccessor; | ||
|
||
/** | ||
* @param array $defaultContext | ||
*/ | ||
public function __construct($defaultContext = []) | ||
{ | ||
$this->propertyAccessor = PropertyAccess::createPropertyAccessor(); | ||
if (!\is_array($defaultContext)) { | ||
@trigger_error(sprintf('Passing an integer as first parameter of the "%s()" method is deprecated since Symfony 4.2, use the "json_encode_options" key of the context instead.', __METHOD__), E_USER_DEPRECATED); | ||
|
||
|
@@ -48,8 +52,12 @@ public function __construct($defaultContext = []) | |
public function encode($data, $format, array $context = []) | ||
{ | ||
$jsonEncodeOptions = $context[self::OPTIONS] ?? $this->defaultContext[self::OPTIONS]; | ||
$encodedJson = json_encode($data, $jsonEncodeOptions); | ||
$propertyPath = $context[JsonEncoder::JSON_PROPERTY_PATH] ?? $this->defaultContext[JsonEncoder::JSON_PROPERTY_PATH]; | ||
|
||
if ($propertyPath) { | ||
$data = $this->wrapEncodableData($propertyPath, $data); | ||
} | ||
$encodedJson = json_encode($data, $jsonEncodeOptions); | ||
if (JSON_ERROR_NONE !== json_last_error() && (false === $encodedJson || !($jsonEncodeOptions & JSON_PARTIAL_OUTPUT_ON_ERROR))) { | ||
throw new NotEncodableValueException(json_last_error_msg()); | ||
} | ||
|
@@ -64,4 +72,21 @@ public function supportsEncoding($format) | |
{ | ||
return JsonEncoder::FORMAT === $format; | ||
} | ||
|
||
/** | ||
< 8000 td class="blob-code blob-code-addition js-file-line"> * Wrap data before encoding. | ||
* | ||
* @param string $propertyPath | ||
* @param mixed $data | ||
* | ||
* @return array | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the docblock should be removed in favor of real types |
||
*/ | ||
private function wrapEncodableData($propertyPath, $data) | ||
{ | ||
$wrappedData = array(); | ||
|
||
$this->propertyAccessor->setValue($wrappedData, $propertyPath, $data); | ||
|
||
return $wrappedData; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -49,10 +49,15 @@ public function decodeProvider() | |
|
||
$assoc = ['foo' => 'bar']; | ||
|
||
return [ | ||
['{"foo": "bar"}', $stdClass, []], | ||
['{"foo": "bar"}', $assoc, ['json_decode_associative' => true]], | ||
]; | ||
return array( | ||
array('{"foo": "bar"}', $stdClass, array()), | ||
array('{"foo": "bar"}', $assoc, array('json_decode_associative' => true)), | ||
array('{"baz": {"foo": "bar"}}', $stdClass, array(JsonEncoder::JSON_PROPERTY_PATH => 'baz')), | ||
array('{"baz": {"foo": "bar"}}', null, array(JsonEncoder::JSON_PROPERTY_PATH => 'baz.inner')), | ||
array('{"baz": {"foo": "bar"}}', $assoc, array(JsonEncoder::JSON_PROPERTY_PATH => '[baz]', 'json_decode_associative' => true)), | ||
array('{"baz": {"foo": "bar"}}', $assoc, array(JsonEncoder::JSON_PROPERTY_PATH => '[baz]', 'json_decode_associative' => true)), | ||
array('{"baz": {"foo": "bar", "inner": {"key": "value"}}}', array('key' => 'value'), array(JsonEncoder::JSON_PROPERTY_PATH => '[baz][inner]', 'json_decode_associative' => true)), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. see fabbot |
||
); | ||
} | ||
|
||
/** | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It adds a hard dependency on property access which is still a suggested dependency in the composer