Skip to content

Commit 7aff669

Browse files
committed
feature #59290 [JsonEncoder] Replace normalizers by value transformers (mtarld)
This PR was merged into the 7.3 branch. Discussion ---------- [JsonEncoder] Replace normalizers by value transformers | Q | A | ------------- | --- | Branch? | 7.3 | Bug fix? | no | New feature? | yes | Deprecations? | no | Issues | Fix #59288 | License | MIT For a better DX and to avoid confusion with Serializer's normalizers and denormalizers: 1. Merge the `NormalizerInterface` and `DenormalizerInterface` to a single `ValueTransformerInterface`: ```php interface ValueTransformerInterface { public function transform(mixed $value, array $options = []): mixed; public static function getJsonValueType(): Type; } ``` 2. Replace the `Normalizer` and `Denormalizer` attributes by a single `ValueTransformer` attribute: ```php #[\Attribute(\Attribute::TARGET_PROPERTY)] class ValueTransformer { // ... public function __construct( callable|string|null $toNativeValue = null, // can be a callable or a ValueTransformerInterface service id callable|string|null $toJsonValue = null, // can be a callable or a ValueTransformerInterface service id ) { // ... } // ... ``` /cc `@wouterj` Commits ------- ced7191fc08 [JsonEncoder] Replace normalizers by value transformers
2 parents 81758d2 + 8951bc8 commit 7aff669

File tree

7 files changed

+60
-46
lines changed

7 files changed

+60
-46
lines changed

DependencyInjection/Compiler/UnusedTagsPass.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,7 @@ class UnusedTagsPass implements CompilerPassInterface
5353
'form.type_guesser',
5454
'html_sanitizer',
5555
'http_client.client',
56-
'json_encoder.denormalizer',
57-
'json_encoder.normalizer',
56+
'json_encoder.value_transformer',
5857
'kernel.cache_clearer',
5958
'kernel.cache_warmer',
6059
'kernel.event_listener',

DependencyInjection/FrameworkExtension.php

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -101,11 +101,10 @@
101101
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
102102
use Symfony\Component\HttpKernel\Log\DebugLoggerConfigurator;
103103
use Symfony\Component\JsonEncoder\Attribute\JsonEncodable;
104-
use Symfony\Component\JsonEncoder\Decode\Denormalizer\DenormalizerInterface as JsonEncoderDenormalizerInterface;
105104
use Symfony\Component\JsonEncoder\DecoderInterface as JsonEncoderDecoderInterface;
106-
use Symfony\Component\JsonEncoder\Encode\Normalizer\NormalizerInterface as JsonEncoderNormalizerInterface;
107105
use Symfony\Component\JsonEncoder\EncoderInterface as JsonEncoderEncoderInterface;
108106
use Symfony\Component\JsonEncoder\JsonEncoder;
107+
use Symfony\Component\JsonEncoder\ValueTransformer\ValueTransformerInterface;
109108
use Symfony\Component\Lock\LockFactory;
110109
use Symfony\Component\Lock\LockInterface;
111110
use Symfony\Component\Lock\PersistingStoreInterface;
@@ -2040,10 +2039,8 @@ private function registerJsonEncoderConfiguration(array $config, ContainerBuilde
20402039
throw new LogicException('JsonEncoder support cannot be enabled as the JsonEncoder component is not installed. Try running "composer require symfony/json-encoder".');
20412040
}
20422041

2043-
$container->registerForAutoconfiguration(JsonEncoderNormalizerInterface::class)
2044-
->addTag('json_encoder.normalizer');
2045-
$container->registerForAutoconfiguration(JsonEncoderDenormalizerInterface::class)
2046-
->addTag('json_encoder.denormalizer');
2042+
$container->registerForAutoconfiguration(ValueTransformerInterface::class)
2043+
->addTag('json_encoder.value_transformer');
20472044

20482045
$loader->load('json_encoder.php');
20492046

Resources/config/json_encoder.php

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@
1313

1414
use Symfony\Component\JsonEncoder\CacheWarmer\EncoderDecoderCacheWarmer;
1515
use Symfony\Component\JsonEncoder\CacheWarmer\LazyGhostCacheWarmer;
16-
use Symfony\Component\JsonEncoder\Decode\Denormalizer\DateTimeDenormalizer;
17-
use Symfony\Component\JsonEncoder\Encode\Normalizer\DateTimeNormalizer;
1816
use Symfony\Component\JsonEncoder\JsonDecoder;
1917
use Symfony\Component\JsonEncoder\JsonEncoder;
2018
use Symfony\Component\JsonEncoder\Mapping\Decode\AttributePropertyMetadataLoader as DecodeAttributePropertyMetadataLoader;
@@ -23,19 +21,21 @@
2321
use Symfony\Component\JsonEncoder\Mapping\Encode\DateTimeTypePropertyMetadataLoader as EncodeDateTimeTypePropertyMetadataLoader;
2422
use Symfony\Component\JsonEncoder\Mapping\GenericTypePropertyMetadataLoader;
2523
use Symfony\Component\JsonEncoder\Mapping\PropertyMetadataLoader;
24+
use Symfony\Component\JsonEncoder\ValueTransformer\DateTimeToStringValueTransformer;
25+
use Symfony\Component\JsonEncoder\ValueTransformer\StringToDateTimeValueTransformer;
2626

2727
return static function (ContainerConfigurator $container) {
2828
$container->services()
2929
// encoder/decoder
3030
->set('json_encoder.encoder', JsonEncoder::class)
3131
->args([
32-
tagged_locator('json_encoder.normalizer'),
32+
tagged_locator('json_encoder.value_transformer'),
3333
service('json_encoder.encode.property_metadata_loader'),
3434
param('.json_encoder.encoders_dir'),
3535
])
3636
->set('json_encoder.decoder', JsonDecoder::class)
3737
->args([
38-
tagged_locator('json_encoder.denormalizer'),
38+
tagged_locator('json_encoder.value_transformer'),
3939
service('json_encoder.decode.property_metadata_loader'),
4040
param('.json_encoder.decoders_dir'),
4141
param('.json_encoder.lazy_ghosts_dir'),
@@ -63,7 +63,7 @@
6363
->decorate('json_encoder.encode.property_metadata_loader')
6464
->args([
6565
service('.inner'),
66-
tagged_locator('json_encoder.normalizer'),
66+
tagged_locator('json_encoder.value_transformer'),
6767
service('type_info.resolver'),
6868
])
6969

@@ -86,23 +86,16 @@
8686
->decorate('json_encoder.decode.property_metadata_loader')
8787
->args([
8888
service('.inner'),
89-
tagged_locator('json_encoder.normalizer'),
89+
tagged_locator('json_encoder.value_transformer'),
9090
service('type_info.resolver'),
9191
])
9292

93-
// normalizers/denormalizers
94-
->set('json_encoder.normalizer.date_time', DateTimeNormalizer::class)
95-
->tag('json_encoder.normalizer')
96-
->set('json_encoder.denormalizer.date_time', DateTimeDenormalizer::class)
97-
->args([
98-
false,
99-
])
100-
->tag('json_encoder.denormalizer')
101-
->set('json_encoder.denormalizer.date_time_immutable', DateTimeDenormalizer::class)
102-
->args([
103-
true,
104-
])
105-
->tag('json_encoder.denormalizer')
93+
// value transformers
94+
->set('json_encoder.value_transformer.date_time_to_string', DateTimeToStringValueTransformer::class)
95+
->tag('json_encoder.value_transformer')
96+
97+
->set('json_encoder.value_transformer.string_to_date_time', StringToDateTimeValueTransformer::class)
98+
->tag('json_encoder.value_transformer')
10699

107100
// cache
108101
->set('.json_encoder.cache_warmer.encoder_decoder', EncoderDecoderCacheWarmer::class)

Tests/Functional/app/JsonEncoder/Dto/Dummy.php

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@
1111

1212
namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\Dto;
1313

14-
use Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\RangeNormalizer;
15-
use Symfony\Component\JsonEncoder\Attribute\Denormalizer;
14+
use Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\RangeToStringValueTransformer;
15+
use Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\StringToRangeValueTransformer;
1616
use Symfony\Component\JsonEncoder\Attribute\EncodedName;
1717
use Symfony\Component\JsonEncoder\Attribute\JsonEncodable;
18-
use Symfony\Component\JsonEncoder\Attribute\Normalizer;
18+
use Symfony\Component\JsonEncoder\Attribute\ValueTransformer;
1919

2020
/**
2121
* @author Mathias Arlaud <mathias.arlaud@gmail.com>
@@ -24,11 +24,9 @@
2424
class Dummy
2525
{
2626
#[EncodedName('@name')]
27-
#[Normalizer('strtoupper')]
28-
#[Denormalizer('strtolower')]
27+
#[ValueTransformer(toJsonValue: 'strtoupper', toNativeValue: 'strtolower')]
2928
public string $name = 'dummy';
3029

31-
#[Normalizer(RangeNormalizer::class)]
32-
#[Denormalizer(RangeNormalizer::class)]
30+
#[ValueTransformer(toJsonValue: RangeToStringValueTransformer::class, toNativeValue: StringToRangeValueTransformer::class)]
3331
public array $range = [10, 20];
3432
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder;
13+
14+
use Symfony\Component\JsonEncoder\ValueTransformer\ValueTransformerInterface;
15+
use Symfony\Component\TypeInfo\Type;
16+
use Symfony\Component\TypeInfo\Type\BuiltinType;
17+
18+
/**
19+
* @author Mathias Arlaud <mathias.arlaud@gmail.com>
20+
*/
21+
class RangeToStringValueTransformer implements ValueTransformerInterface
22+
{
23+
public function transform(mixed $value, array $options = []): string
24+
{
25+
return $value[0].'..'.$value[1];
26+
}
27+
28+
public static function getJsonValueType(): BuiltinType
29+
{
30+
return Type::string();
31+
}
32+
}

Tests/Functional/app/JsonEncoder/RangeNormalizer.php renamed to Tests/Functional/app/JsonEncoder/StringToRangeValueTransformer.php

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,27 +11,21 @@
1111

1212
namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder;
1313

14-
use Symfony\Component\JsonEncoder\Decode\Denormalizer\DenormalizerInterface;
15-
use Symfony\Component\JsonEncoder\Encode\Normalizer\NormalizerInterface;
14+
use Symfony\Component\JsonEncoder\ValueTransformer\ValueTransformerInterface;
1615
use Symfony\Component\TypeInfo\Type;
1716
use Symfony\Component\TypeInfo\Type\BuiltinType;
1817

1918
/**
2019
* @author Mathias Arlaud <mathias.arlaud@gmail.com>
2120
*/
22-
class RangeNormalizer implements NormalizerInterface, DenormalizerInterface
21+
class StringToRangeValueTransformer implements ValueTransformerInterface
2322
{
24-
public function normalize(mixed $denormalized, array $options = []): string
23+
public function transform(mixed $value, array $options = []): array
2524
{
26-
return $denormalized[0].'..'.$denormalized[1];
25+
return array_map(static fn (string $v): int => (int) $v, explode('..', $value));
2726
}
2827

29-
public function denormalize(mixed $normalized, array $options = []): array
30-
{
31-
return array_map(static fn (string $v): int => (int) $v, explode('..', $normalized));
32-
}
33-
34-
public static function getNormalizedType(): BuiltinType
28+
public static function getJsonValueType(): BuiltinType
3529
{
3630
return Type::string();
3731
}

Tests/Functional/app/JsonEncoder/config.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,5 @@ services:
2323
public: true
2424

2525
Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\Dto\Dummy: ~
26-
Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\RangeNormalizer: ~
26+
Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\StringToRangeValueTransformer: ~
27+
Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\RangeToStringValueTransformer: ~

0 commit comments

Comments
 (0)