Skip to content

Commit

Permalink
feat: add support for JSON_FORCE_OBJECT option in JSON normalizer
Browse files Browse the repository at this point in the history
```php
(new \CuyZ\Valinor\MapperBuilder())
    ->normalizer(Format::json())
    ->withOptions(JSON_FORCE_OBJECT)
    ->normalize(['foo', 'bar']);

// {"0":"foo","1":"bar"}
```
  • Loading branch information
simPod committed Sep 16, 2024
1 parent 304db39 commit f3e8c1e
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 7 deletions.
8 changes: 6 additions & 2 deletions src/Normalizer/Formatter/JsonFormatter.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use function is_scalar;
use function json_encode;

use const JSON_FORCE_OBJECT;
use const JSON_THROW_ON_ERROR;

/** @internal */
Expand Down Expand Up @@ -54,8 +55,11 @@ public function format(mixed $value): void
// afterward, this leads to a JSON array being written, while it
// should have been an object. This is a trade-off we accept,
// considering most generators starting at 0 are actually lists.
$isList = ($value instanceof Generator && $value->key() === 0)
|| (is_array($value) && array_is_list($value));
$isList = ! ($this->jsonEncodingOptions & JSON_FORCE_OBJECT)
&& (
($value instanceof Generator && $value->key() === 0)
|| (is_array($value) && array_is_list($value))
);

$isFirst = true;

Expand Down
4 changes: 3 additions & 1 deletion src/Normalizer/JsonNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use function is_resource;
use function stream_get_contents;

use const JSON_FORCE_OBJECT;
use const JSON_HEX_AMP;
use const JSON_HEX_APOS;
use const JSON_HEX_QUOT;
Expand All @@ -34,7 +35,8 @@
*/
final class JsonNormalizer implements Normalizer
{
private const ACCEPTABLE_JSON_OPTIONS = JSON_HEX_QUOT
private const ACCEPTABLE_JSON_OPTIONS = JSON_FORCE_OBJECT
| JSON_HEX_QUOT
| JSON_HEX_TAG
| JSON_HEX_AMP
| JSON_HEX_APOS
Expand Down
21 changes: 17 additions & 4 deletions tests/Integration/Normalizer/NormalizerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

use function array_merge;

use const JSON_FORCE_OBJECT;
use const JSON_HEX_TAG;
use const JSON_THROW_ON_ERROR;

Expand Down Expand Up @@ -194,6 +195,21 @@ public static function normalize_basic_values_yields_expected_output_data_provid
'expected json' => '{"foo":"foo","bar":"bar"}',
];

yield 'list' => [
'input' => ['foo', 'bar'],
'expected array' => ['foo', 'bar'],
'expected json' => '["foo","bar"]',
];

yield 'list kept as object in json' => [
'input' => ['foo', 'bar'],
'expected array' => ['foo', 'bar'],
'expected json' => '{"0":"foo","1":"bar"}',
[],
[],
JSON_FORCE_OBJECT
];

yield 'ArrayObject' => [
'input' => new ArrayObject(['foo' => 'foo', 'bar' => 'bar']),
'expected array' => [
Expand Down Expand Up @@ -1148,16 +1164,13 @@ public function test_json_transformer_will_always_throw_on_error(): void

public function test_json_transformer_only_accepts_acceptable_json_options(): void
{
$normalizer = $this->mapperBuilder()->normalizer(Format::json())->withOptions(JSON_FORCE_OBJECT);
self::assertSame(JSON_THROW_ON_ERROR, (fn () => $this->jsonEncodingOptions)->call($normalizer));

$normalizer = $this->mapperBuilder()->normalizer(Format::json())->withOptions(JSON_PARTIAL_OUTPUT_ON_ERROR);
self::assertSame(JSON_THROW_ON_ERROR, (fn () => $this->jsonEncodingOptions)->call($normalizer));

$normalizer = $this->mapperBuilder()->normalizer(Format::json())->withOptions(JSON_PRETTY_PRINT);
self::assertSame(JSON_THROW_ON_ERROR, (fn () => $this->jsonEncodingOptions)->call($normalizer));

$normalizer = $this->mapperBuilder()->normalizer(Format::json())->withOptions(JSON_FORCE_OBJECT | JSON_PARTIAL_OUTPUT_ON_ERROR | JSON_PRETTY_PRINT);
$normalizer = $this->mapperBuilder()->normalizer(Format::json())->withOptions(JSON_PARTIAL_OUTPUT_ON_ERROR | JSON_PRETTY_PRINT);
self::assertSame(JSON_THROW_ON_ERROR, (fn () => $this->jsonEncodingOptions)->call($normalizer));
}
}
Expand Down

0 comments on commit f3e8c1e

Please sign in to comment.