From d1d1ceb38e171132b6d13e19cd5576bd0dc85cd8 Mon Sep 17 00:00:00 2001 From: Fabien Bourigault Date: Tue, 18 Sep 2018 09:07:34 +0200 Subject: [PATCH] [Serialized] allow configuring the serialized name of properties through metadata --- .../FrameworkExtension.php | 4 +- .../Resources/config/serializer.xml | 6 +- .../FrameworkExtensionTest.php | 2 +- .../Bundle/FrameworkBundle/composer.json | 2 +- .../Serializer/Annotation/SerializedName.php | 48 +++++++ src/Symfony/Component/Serializer/CHANGELOG.md | 1 + .../Serializer/Mapping/AttributeMetadata.php | 32 ++++- .../Mapping/AttributeMetadataInterface.php | 10 ++ .../Mapping/Loader/AnnotationLoader.php | 9 ++ .../Mapping/Loader/XmlFileLoader.php | 4 + .../Mapping/Loader/YamlFileLoader.php | 8 ++ .../serializer-mapping-1.0.xsd | 7 ++ .../MetadataAwareNameConverter.php | 119 ++++++++++++++++++ .../Tests/Annotation/SerializedNameTest.php | 55 ++++++++ .../Tests/Fixtures/SerializedNameDummy.php | 47 +++++++ .../Tests/Fixtures/serialization.xml | 5 + .../Tests/Fixtures/serialization.yml | 6 + .../Tests/Mapping/AttributeMetadataTest.php | 11 ++ .../Mapping/Loader/AnnotationLoaderTest.php | 10 ++ .../Mapping/Loader/XmlFileLoaderTest.php | 10 ++ .../Mapping/Loader/YamlFileLoaderTest.php | 10 ++ .../MetadataAwareNameConverterTest.php | 116 +++++++++++++++++ 22 files changed, 516 insertions(+), 6 deletions(-) create mode 100644 src/Symfony/Component/Serializer/Annotation/SerializedName.php create mode 100644 src/Symfony/Component/Serializer/NameConverter/MetadataAwareNameConverter.php create mode 100644 src/Symfony/Component/Serializer/Tests/Annotation/SerializedNameTest.php create mode 100644 src/Symfony/Component/Serializer/Tests/Fixtures/SerializedNameDummy.php create mode 100644 src/Symfony/Component/Serializer/Tests/NameConverter/MetadataAwareNameConverterTest.php diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index a3e6a3116aaa0..d09b0afb9d49f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -74,8 +74,8 @@ use Symfony\Component\PropertyAccess\PropertyAccessor; use Symfony\Component\PropertyInfo\PropertyAccessExtractorInterface; use Symfony\Component\PropertyInfo\PropertyDescriptionExtractorInterface; -use Symfony\Component\PropertyInfo\PropertyInitializableExtractorInterface; use Symfony\Component\PropertyInfo\PropertyInfoExtractorInterface; +use Symfony\Component\PropertyInfo\PropertyInitializableExtractorInterface; use Symfony\Component\PropertyInfo\PropertyListExtractorInterface; use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface; use Symfony\Component\Routing\Loader\AnnotationDirectoryLoader; @@ -1363,7 +1363,7 @@ private function registerSerializerConfiguration(array $config, ContainerBuilder } if (isset($config['name_converter']) && $config['name_converter']) { - $container->getDefinition('serializer.normalizer.object')->replaceArgument(1, new Reference($config['name_converter'])); + $container->getDefinition('serializer.name_converter.metadata_aware')->setArgument(1, new Reference($config['name_converter'])); } if (isset($config['circular_reference_handler']) && $config['circular_reference_handler']) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.xml index d2ac13fe7279f..2cd70ff083894 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.xml @@ -58,7 +58,7 @@ - null + @@ -119,6 +119,10 @@ + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php index 1c6e54438e1c9..260f3819900e0 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php @@ -976,7 +976,7 @@ public function testSerializerEnabled() $this->assertCount(2, $argument); $this->assertEquals('Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader', $argument[0]->getClass()); $this->assertNull($container->getDefinition('serializer.mapping.class_metadata_factory')->getArgument(1)); - $this->assertEquals(new Reference('serializer.name_converter.camel_case_to_snake_case'), $container->getDefinition('serializer.normalizer.object')->getArgument(1)); + $this->assertEquals(new Reference('serializer.name_converter.camel_case_to_snake_case'), $container->getDefinition('serializer.name_converter.metadata_aware')->getArgument(1)); $this->assertEquals(new Reference('property_info', ContainerBuilder::IGNORE_ON_INVALID_REFERENCE), $container->getDefinition('serializer.normalizer.object')->getArgument(3)); $this->assertEquals(array('setCircularReferenceHandler', array(new Reference('my.circular.reference.handler'))), $container->getDefinition('serializer.normalizer.object')->getMethodCalls()[0]); $this->assertEquals(array('setMaxDepthHandler', array(new Reference('my.max.depth.handler'))), $container->getDefinition('serializer.normalizer.object')->getMethodCalls()[1]); diff --git a/src/Symfony/Bundle/FrameworkBundle/composer.json b/src/Symfony/Bundle/FrameworkBundle/composer.json index a3d2f6ab3656d..ed47758fa0003 100644 --- a/src/Symfony/Bundle/FrameworkBundle/composer.json +++ b/src/Symfony/Bundle/FrameworkBundle/composer.json @@ -45,7 +45,7 @@ "symfony/process": "~3.4|~4.0", "symfony/security-core": "~3.4|~4.0", "symfony/security-csrf": "~3.4|~4.0", - "symfony/serializer": "^4.1", + "symfony/serializer": "^4.2", "symfony/stopwatch": "~3.4|~4.0", "symfony/translation": "~4.2", "symfony/templating": "~3.4|~4.0", diff --git a/src/Symfony/Component/Serializer/Annotation/SerializedName.php b/src/Symfony/Component/Serializer/Annotation/SerializedName.php new file mode 100644 index 0000000000000..2de4a2c42274d --- /dev/null +++ b/src/Symfony/Component/Serializer/Annotation/SerializedName.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer\Annotation; + +use Symfony\Component\Serializer\Exception\InvalidArgumentException; + +/** + * Annotation class for @SerializedName(). + * + * @Annotation + * @Target({"PROPERTY", "METHOD"}) + * + * @author Fabien Bourigault + */ +final class SerializedName +{ + /** + * @var string + */ + private $serializedName; + + public function __construct(array $data) + { + if (!isset($data['value'])) { + throw new InvalidArgumentException(sprintf('Parameter of annotation "%s" should be set.', \get_class($this))); + } + + if (!\is_string($data['value']) || empty($data['value'])) { + throw new InvalidArgumentException(sprintf('Parameter of annotation "%s" must be a non-empty string.', \get_class($this))); + } + + $this->serializedName = $data['value']; + } + + public function getSerializedName(): string + { + return $this->serializedName; + } +} diff --git a/src/Symfony/Component/Serializer/CHANGELOG.md b/src/Symfony/Component/Serializer/CHANGELOG.md index 85c6abf5e9e14..84fbbfde6177c 100644 --- a/src/Symfony/Component/Serializer/CHANGELOG.md +++ b/src/Symfony/Component/Serializer/CHANGELOG.md @@ -22,6 +22,7 @@ CHANGELOG either `EncoderInterface` or `DecoderInterface` * added the optional `$objectClassResolver` argument in `AbstractObjectNormalizer` and `ObjectNormalizer` constructor + * added `MetadataAwareNameConverter` to configure the serialized name of properties through metadata 4.1.0 ----- diff --git a/src/Symfony/Component/Serializer/Mapping/AttributeMetadata.php b/src/Symfony/Component/Serializer/Mapping/AttributeMetadata.php index f0fd5f2a6ad48..9e8ebc6524c72 100644 --- a/src/Symfony/Component/Serializer/Mapping/AttributeMetadata.php +++ b/src/Symfony/Component/Serializer/Mapping/AttributeMetadata.php @@ -41,6 +41,15 @@ class AttributeMetadata implements AttributeMetadataInterface */ public $maxDepth; + /** + * @var string|null + * + * @internal This property is public in order to reduce the size of the + * class' serialized representation. Do not access it. Use + * {@link getSerializedName()} instead. + */ + public $serializedName; + public function __construct(string $name) { $this->name = $name; @@ -88,6 +97,22 @@ public function getMaxDepth() return $this->maxDepth; } + /** + * {@inheritdoc} + */ + public function setSerializedName(string $serializedName = null) + { + $this->serializedName = $serializedName; + } + + /** + * {@inheritdoc} + */ + public function getSerializedName(): ?string + { + return $this->serializedName; + } + /** * {@inheritdoc} */ @@ -101,6 +126,11 @@ public function merge(AttributeMetadataInterface $attributeMetadata) if (null === $this->maxDepth) { $this->maxDepth = $attributeMetadata->getMaxDepth(); } + + // Overwrite only if not defined + if (null === $this->serializedName) { + $this->serializedName = $attributeMetadata->getSerializedName(); + } } /** @@ -110,6 +140,6 @@ public function merge(AttributeMetadataInterface $attributeMetadata) */ public function __sleep() { - return array('name', 'groups', 'maxDepth'); + return array('name', 'groups', 'maxDepth', 'serializedName'); } } diff --git a/src/Symfony/Component/Serializer/Mapping/AttributeMetadataInterface.php b/src/Symfony/Component/Serializer/Mapping/AttributeMetadataInterface.php index d9a15d5ac0de5..bbbde922a8e9c 100644 --- a/src/Symfony/Component/Serializer/Mapping/AttributeMetadataInterface.php +++ b/src/Symfony/Component/Serializer/Mapping/AttributeMetadataInterface.php @@ -57,6 +57,16 @@ public function setMaxDepth($maxDepth); */ public function getMaxDepth(); + /** + * Sets the serialization name for this attribute. + */ + public function setSerializedName(string $serializedName = null); + + /** + * Gets the serialization name for this attribute. + */ + public function getSerializedName(): ?string; + /** * Merges an {@see AttributeMetadataInterface} with in the current one. */ diff --git a/src/Symfony/Component/Serializer/Mapping/Loader/AnnotationLoader.php b/src/Symfony/Component/Serializer/Mapping/Loader/AnnotationLoader.php index 0c195f671dad9..bd9fab1c27777 100644 --- a/src/Symfony/Component/Serializer/Mapping/Loader/AnnotationLoader.php +++ b/src/Symfony/Component/Serializer/Mapping/Loader/AnnotationLoader.php @@ -15,6 +15,7 @@ use Symfony\Component\Serializer\Annotation\DiscriminatorMap; use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Serializer\Annotation\MaxDepth; +use Symfony\Component\Serializer\Annotation\SerializedName; use Symfony\Component\Serializer\Exception\MappingException; use Symfony\Component\Serializer\Mapping\AttributeMetadata; use Symfony\Component\Serializer\Mapping\ClassDiscriminatorMapping; @@ -68,6 +69,8 @@ public function loadClassMetadata(ClassMetadataInterface $classMetadata) } } elseif ($annotation instanceof MaxDepth) { $attributesMetadata[$property->name]->setMaxDepth($annotation->getMaxDepth()); + } elseif ($annotation instanceof SerializedName) { + $attributesMetadata[$property->name]->setSerializedName($annotation->getSerializedName()); } $loaded = true; @@ -107,6 +110,12 @@ public function loadClassMetadata(ClassMetadataInterface $classMetadata) } $attributeMetadata->setMaxDepth($annotation->getMaxDepth()); + } elseif ($annotation instanceof SerializedName) { + if (!$accessorOrMutator) { + throw new MappingException(sprintf('SerializedName on "%s::%s" cannot be added. SerializedName can only be added on methods beginning with "get", "is", "has" or "set".', $className, $method->name)); + } + + $attributeMetadata->setSerializedName($annotation->getSerializedName()); } $loaded = true; diff --git a/src/Symfony/Component/Serializer/Mapping/Loader/XmlFileLoader.php b/src/Symfony/Component/Serializer/Mapping/Loader/XmlFileLoader.php index eec766f91d533..d4d5e82e6000e 100644 --- a/src/Symfony/Component/Serializer/Mapping/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/Serializer/Mapping/Loader/XmlFileLoader.php @@ -66,6 +66,10 @@ public function loadClassMetadata(ClassMetadataInterface $classMetadata) if (isset($attribute['max-depth'])) { $attributeMetadata->setMaxDepth((int) $attribute['max-depth']); } + + if (isset($attribute['serialized-name'])) { + $attributeMetadata->setSerializedName((string) $attribute['serialized-name']); + } } if (isset($xml->{'discriminator-map'})) { diff --git a/src/Symfony/Component/Serializer/Mapping/Loader/YamlFileLoader.php b/src/Symfony/Component/Serializer/Mapping/Loader/YamlFileLoader.php index 10345afc0595d..7aa761485faf1 100644 --- a/src/Symfony/Component/Serializer/Mapping/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/Serializer/Mapping/Loader/YamlFileLoader.php @@ -85,6 +85,14 @@ public function loadClassMetadata(ClassMetadataInterface $classMetadata) $attributeMetadata->setMaxDepth($data['max_depth']); } + + if (isset($data['serialized_name'])) { + if (!\is_string($data['serialized_name']) || empty($data['serialized_name'])) { + throw new MappingException(sprintf('The "serialized_name" value must be a non-empty string in "%s" for the attribute "%s" of the class "%s".', $this->file, $attribute, $classMetadata->getName())); + } + + $attributeMetadata->setSerializedName($data['serialized_name']); + } } } diff --git a/src/Symfony/Component/Serializer/Mapping/Loader/schema/dic/serializer-mapping/serializer-mapping-1.0.xsd b/src/Symfony/Component/Serializer/Mapping/Loader/schema/dic/serializer-mapping/serializer-mapping-1.0.xsd index 14eff8c4dea7f..5dfe1e3730041 100644 --- a/src/Symfony/Component/Serializer/Mapping/Loader/schema/dic/serializer-mapping/serializer-mapping-1.0.xsd +++ b/src/Symfony/Component/Serializer/Mapping/Loader/schema/dic/serializer-mapping/serializer-mapping-1.0.xsd @@ -71,6 +71,13 @@ + + + + + + + diff --git a/src/Symfony/Component/Serializer/NameConverter/MetadataAwareNameConverter.php b/src/Symfony/Component/Serializer/NameConverter/MetadataAwareNameConverter.php new file mode 100644 index 0000000000000..52f8ff3d4e1eb --- /dev/null +++ b/src/Symfony/Component/Serializer/NameConverter/MetadataAwareNameConverter.php @@ -0,0 +1,119 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer\NameConverter; + +use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface; + +/** + * @author Fabien Bourigault + */ +final class MetadataAwareNameConverter implements AdvancedNameConverterInterface +{ + private $metadataFactory; + + /** + * @var NameConverterInterface|AdvancedNameConverterInterface|null + */ + private $fallbackNameConverter; + + private static $normalizeCache = array(); + + private static $denormalizeCache = array(); + + private static $attributesMetadataCache = array(); + + public function __construct(ClassMetadataFactoryInterface $metadataFactory, NameConverterInterface $fallbackNameConverter = null) + { + $this->metadataFactory = $metadataFactory; + $this->fallbackNameConverter = $fallbackNameConverter; + } + + /** + * {@inheritdoc} + */ + public function normalize($propertyName, string $class = null, string $format = null, array $context = array()) + { + if (null === $class) { + return $this->normalizeFallback($propertyName, $class, $format, $context); + } + + if (!isset(self::$normalizeCache[$class][$propertyName])) { + self::$normalizeCache[$class][$propertyName] = $this->getCacheValueForNormalization($propertyName, $class); + } + + return self::$normalizeCache[$class][$propertyName] ?? $this->normalizeFallback($propertyName, $class, $format, $context); + } + + /** + * {@inheritdoc} + */ + public function denormalize($propertyName, string $class = null, string $format = null, array $context = array()) + { + if (null === $class) { + return $this->denormalizeFallback($propertyName, $class, $format, $context); + } + + if (!isset(self::$denormalizeCache[$class][$propertyName])) { + self::$denormalizeCache[$class][$propertyName] = $this->getCacheValueForDenormalization($propertyName, $class); + } + + return self::$denormalizeCache[$class][$propertyName] ?? $this->denormalizeFallback($propertyName, $class, $format, $context); + } + + private function getCacheValueForNormalization(string $propertyName, string $class): ?string + { + if (!$this->metadataFactory->hasMetadataFor($class)) { + return null; + } + + return $this->metadataFactory->getMetadataFor($class)->getAttributesMetadata()[$propertyName]->getSerializedName() ?? null; + } + + private function normalizeFallback(string $propertyName, string $class = null, string $format = null, array $context = array()): string + { + return $this->fallbackNameConverter ? $this->fallbackNameConverter->normalize($propertyName, $class, $format, $context) : $propertyName; + } + + private function getCacheValueForDenormalization(string $propertyName, string $class): ?string + { + if (!isset(self::$attributesMetadataCache[$class])) { + self::$attributesMetadataCache[$class] = $this->getCacheValueForAttributesMetadata($class); + } + + return self::$attributesMetadataCache[$class][$propertyName] ?? null; + } + + private function denormalizeFallback(string $propertyName, string $class = null, string $format = null, array $context = array()): string + { + return $this->fallbackNameConverter ? $this->fallbackNameConverter->denormalize($propertyName, $class, $format, $context) : $propertyName; + } + + private function getCacheValueForAttributesMetadata(string $class): array + { + if (!$this->metadataFactory->hasMetadataFor($class)) { + return array(); + } + + $classMetadata = $this->metadataFactory->getMetadataFor($class); + + $cache = array(); + foreach ($classMetadata->getAttributesMetadata() as $name => $metadata) { + if (null === $metadata->getSerializedName()) { + continue; + } + + $cache[$metadata->getSerializedName()] = $name; + } + + return $cache; + } +} diff --git a/src/Symfony/Component/Serializer/Tests/Annotation/SerializedNameTest.php b/src/Symfony/Component/Serializer/Tests/Annotation/SerializedNameTest.php new file mode 100644 index 0000000000000..45a18db69bd74 --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Annotation/SerializedNameTest.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer\Tests\Annotation; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Serializer\Annotation\SerializedName; + +/** + * @author Fabien Bourigault + */ +class SerializedNameTest extends TestCase +{ + /** + * @expectedException \Symfony\Component\Serializer\Exception\InvalidArgumentException + * @expectedExceptionMessage Parameter of annotation "Symfony\Component\Serializer\Annotation\SerializedName" should be set. + */ + public function testNotSetSerializedNameParameter() + { + new SerializedName(array()); + } + + public function provideInvalidValues() + { + return array( + array(''), + array(0), + ); + } + + /** + * @dataProvider provideInvalidValues + * + * @expectedException \Symfony\Component\Serializer\Exception\InvalidArgumentException + * @expectedExceptionMessage Parameter of annotation "Symfony\Component\Serializer\Annotation\SerializedName" must be a non-empty string. + */ + public function testNotAStringSerializedNameParameter($value) + { + new SerializedName(array('value' => $value)); + } + + public function testSerializedNameParameters() + { + $maxDepth = new SerializedName(array('value' => 'foo')); + $this->assertEquals('foo', $maxDepth->getSerializedName()); + } +} diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/SerializedNameDummy.php b/src/Symfony/Component/Serializer/Tests/Fixtures/SerializedNameDummy.php new file mode 100644 index 0000000000000..6fed9d201a26e --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Fixtures/SerializedNameDummy.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer\Tests\Fixtures; + +use Symfony\Component\Serializer\Annotation\SerializedName; + +/** + * @author Fabien Bourigault + */ +class SerializedNameDummy +{ + /** + * @SerializedName("baz") + */ + public $foo; + + public $bar; + + public $quux; + + /** + * @var self + */ + public $child; + + /** + * @SerializedName("qux") + */ + public function getBar() + { + return $this->bar; + } + + public function getChild() + { + return $this->child; + } +} diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/serialization.xml b/src/Symfony/Component/Serializer/Tests/Fixtures/serialization.xml index d6f5ce3795ae1..31ac77c47c6b7 100644 --- a/src/Symfony/Component/Serializer/Tests/Fixtures/serialization.xml +++ b/src/Symfony/Component/Serializer/Tests/Fixtures/serialization.xml @@ -20,6 +20,11 @@ + + + + + diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/serialization.yml b/src/Symfony/Component/Serializer/Tests/Fixtures/serialization.yml index a967faf2a6d49..dfde403a64897 100644 --- a/src/Symfony/Component/Serializer/Tests/Fixtures/serialization.yml +++ b/src/Symfony/Component/Serializer/Tests/Fixtures/serialization.yml @@ -10,6 +10,12 @@ max_depth: 2 bar: max_depth: 3 +'Symfony\Component\Serializer\Tests\Fixtures\SerializedNameDummy': + attributes: + foo: + serialized_name: 'baz' + bar: + serialized_name: 'qux' 'Symfony\Component\Serializer\Tests\Fixtures\AbstractDummy': discriminator_map: type_property: type diff --git a/src/Symfony/Component/Serializer/Tests/Mapping/AttributeMetadataTest.php b/src/Symfony/Component/Serializer/Tests/Mapping/AttributeMetadataTest.php index 16ebcb1aac8dc..d3fbb6a34d319 100644 --- a/src/Symfony/Component/Serializer/Tests/Mapping/AttributeMetadataTest.php +++ b/src/Symfony/Component/Serializer/Tests/Mapping/AttributeMetadataTest.php @@ -49,6 +49,14 @@ public function testMaxDepth() $this->assertEquals(69, $attributeMetadata->getMaxDepth()); } + public function testSerializedName() + { + $attributeMetadata = new AttributeMetadata('name'); + $attributeMetadata->setSerializedName('serialized_name'); + + $this->assertEquals('serialized_name', $attributeMetadata->getSerializedName()); + } + public function testMerge() { $attributeMetadata1 = new AttributeMetadata('a1'); @@ -59,11 +67,13 @@ public function testMerge() $attributeMetadata2->addGroup('a'); $attributeMetadata2->addGroup('c'); $attributeMetadata2->setMaxDepth(2); + $attributeMetadata2->setSerializedName('a3'); $attributeMetadata1->merge($attributeMetadata2); $this->assertEquals(array('a', 'b', 'c'), $attributeMetadata1->getGroups()); $this->assertEquals(2, $attributeMetadata1->getMaxDepth()); + $this->assertEquals('a3', $attributeMetadata1->getSerializedName()); } public function testSerialize() @@ -72,6 +82,7 @@ public function testSerialize() $attributeMetadata->addGroup('a'); $attributeMetadata->addGroup('b'); $attributeMetadata->setMaxDepth(3); + $attributeMetadata->setSerializedName('serialized_name'); $serialized = serialize($attributeMetadata); $this->assertEquals($attributeMetadata, unserialize($serialized)); diff --git a/src/Symfony/Component/Serializer/Tests/Mapping/Loader/AnnotationLoaderTest.php b/src/Symfony/Component/Serializer/Tests/Mapping/Loader/AnnotationLoaderTest.php index b6566d333166c..f3148fda28780 100644 --- a/src/Symfony/Component/Serializer/Tests/Mapping/Loader/AnnotationLoaderTest.php +++ b/src/Symfony/Component/Serializer/Tests/Mapping/Loader/AnnotationLoaderTest.php @@ -83,6 +83,16 @@ public function testLoadMaxDepth() $this->assertEquals(3, $attributesMetadata['bar']->getMaxDepth()); } + public function testLoadSerializedName() + { + $classMetadata = new ClassMetadata('Symfony\Component\Serializer\Tests\Fixtures\SerializedNameDummy'); + $this->loader->loadClassMetadata($classMetadata); + + $attributesMetadata = $classMetadata->getAttributesMetadata(); + $this->assertEquals('baz', $attributesMetadata['foo']->getSerializedName()); + $this->assertEquals('qux', $attributesMetadata['bar']->getSerializedName()); + } + public function testLoadClassMetadataAndMerge() { $classMetadata = new ClassMetadata('Symfony\Component\Serializer\Tests\Fixtures\GroupDummy'); diff --git a/src/Symfony/Component/Serializer/Tests/Mapping/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/Serializer/Tests/Mapping/Loader/XmlFileLoaderTest.php index c369f8938d585..1436403522ff9 100644 --- a/src/Symfony/Component/Serializer/Tests/Mapping/Loader/XmlFileLoaderTest.php +++ b/src/Symfony/Component/Serializer/Tests/Mapping/Loader/XmlFileLoaderTest.php @@ -68,6 +68,16 @@ public function testMaxDepth() $this->assertEquals(3, $attributesMetadata['bar']->getMaxDepth()); } + public function testSerializedName() + { + $classMetadata = new ClassMetadata('Symfony\Component\Serializer\Tests\Fixtures\SerializedNameDummy'); + $this->loader->loadClassMetadata($classMetadata); + + $attributesMetadata = $classMetadata->getAttributesMetadata(); + $this->assertEquals('baz', $attributesMetadata['foo']->getSerializedName()); + $this->assertEquals('qux', $attributesMetadata['bar']->getSerializedName()); + } + public function testLoadDiscriminatorMap() { $classMetadata = new ClassMetadata(AbstractDummy::class); diff --git a/src/Symfony/Component/Serializer/Tests/Mapping/Loader/YamlFileLoaderTest.php b/src/Symfony/Component/Serializer/Tests/Mapping/Loader/YamlFileLoaderTest.php index 6e18edbe28655..a463936825c82 100644 --- a/src/Symfony/Component/Serializer/Tests/Mapping/Loader/YamlFileLoaderTest.php +++ b/src/Symfony/Component/Serializer/Tests/Mapping/Loader/YamlFileLoaderTest.php @@ -83,6 +83,16 @@ public function testMaxDepth() $this->assertEquals(3, $attributesMetadata['bar']->getMaxDepth()); } + public function testSerializedName() + { + $classMetadata = new ClassMetadata('Symfony\Component\Serializer\Tests\Fixtures\SerializedNameDummy'); + $this->loader->loadClassMetadata($classMetadata); + + $attributesMetadata = $classMetadata->getAttributesMetadata(); + $this->assertEquals('baz', $attributesMetadata['foo']->getSerializedName()); + $this->assertEquals('qux', $attributesMetadata['bar']->getSerializedName()); + } + public function testLoadDiscriminatorMap() { $classMetadata = new ClassMetadata(AbstractDummy::class); diff --git a/src/Symfony/Component/Serializer/Tests/NameConverter/MetadataAwareNameConverterTest.php b/src/Symfony/Component/Serializer/Tests/NameConverter/MetadataAwareNameConverterTest.php new file mode 100644 index 0000000000000..40fbc396f6ec5 --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/NameConverter/MetadataAwareNameConverterTest.php @@ -0,0 +1,116 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer\Tests\NameConverter; + +use Doctrine\Common\Annotations\AnnotationReader; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory; +use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface; +use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader; +use Symfony\Component\Serializer\NameConverter\MetadataAwareNameConverter; +use Symfony\Component\Serializer\NameConverter\NameConverterInterface; +use Symfony\Component\Serializer\Tests\Fixtures\SerializedNameDummy; + +/** + * @author Fabien Bourigault + */ +final class MetadataAwareNameConverterTest extends TestCase +{ + public function testInterface() + { + $classMetadataFactory = $this->createMock(ClassMetadataFactoryInterface::class); + $nameConverter = new MetadataAwareNameConverter($classMetadataFactory); + $this->assertInstanceOf('Symfony\Component\Serializer\NameConverter\NameConverterInterface', $nameConverter); + } + + /** + * @dataProvider attributeProvider + */ + public function testNormalize($propertyName, $expected) + { + $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader())); + + $nameConverter = new MetadataAwareNameConverter($classMetadataFactory); + + $this->assertEquals($expected, $nameConverter->normalize($propertyName, SerializedNameDummy::class)); + } + + /** + * @dataProvider fallbackAttributeProvider + */ + public function testNormalizeWithFallback($propertyName, $expected) + { + $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader())); + + $fallback = $this->createMock(NameConverterInterface::class); + $fallback + ->method('normalize') + ->willReturnCallback(function ($propertyName) { + return strtoupper($propertyName); + }) + ; + + $nameConverter = new MetadataAwareNameConverter($classMetadataFactory, $fallback); + + $this->assertEquals($expected, $nameConverter->normalize($propertyName, SerializedNameDummy::class)); + } + + /** + * @dataProvider attributeProvider + */ + public function testDenormalize($expected, $propertyName) + { + $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader())); + + $nameConverter = new MetadataAwareNameConverter($classMetadataFactory); + + $this->assertEquals($expected, $nameConverter->denormalize($propertyName, SerializedNameDummy::class)); + } + + /** + * @dataProvider fallbackAttributeProvider + */ + public function testDenormalizeWithFallback($expected, $propertyName) + { + $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader())); + + $fallback = $this->createMock(NameConverterInterface::class); + $fallback + ->method('denormalize') + ->willReturnCallback(function ($propertyName) { + return strtolower($propertyName); + }) + ; + + $nameConverter = new MetadataAwareNameConverter($classMetadataFactory, $fallback); + + $this->assertEquals($expected, $nameConverter->denormalize($propertyName, SerializedNameDummy::class)); + } + + public function attributeProvider() + { + return array( + array('foo', 'baz'), + array('bar', 'qux'), + array('quux', 'quux'), + ); + } + + public function fallbackAttributeProvider() + { + return array( + array('foo', 'baz'), + array('bar', 'qux'), + array('quux', 'QUUX'), + ); + } +} pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy