diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php
index afd3acabc46a2..69dc534e417fb 100644
--- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php
+++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php
@@ -128,8 +128,8 @@ public function __construct(ClassMetadataFactoryInterface $classMetadataFactory
$this->propertyTypeExtractor = $propertyTypeExtractor;
- if (null === $classDiscriminatorResolver && null !== $classMetadataFactory) {
- $classDiscriminatorResolver = new ClassDiscriminatorFromClassMetadata($classMetadataFactory);
+ if ($classMetadataFactory) {
+ $classDiscriminatorResolver ??= new ClassDiscriminatorFromClassMetadata($classMetadataFactory);
}
$this->classDiscriminatorResolver = $classDiscriminatorResolver;
$this->objectClassResolver = $objectClassResolver;
@@ -217,7 +217,7 @@ public function normalize(mixed $object, string $format = null, array $context =
}
$preserveEmptyObjects = $context[self::PRESERVE_EMPTY_OBJECTS] ?? $this->defaultContext[self::PRESERVE_EMPTY_OBJECTS] ?? false;
- if ($preserveEmptyObjects && !\count($data)) {
+ if ($preserveEmptyObjects && !$data) {
return new \ArrayObject();
}
@@ -226,19 +226,8 @@ public function normalize(mixed $object, string $format = null, array $context =
protected function instantiateObject(array &$data, string $class, array &$context, \ReflectionClass $reflectionClass, array|bool $allowedAttributes, string $format = null)
{
- if ($this->classDiscriminatorResolver && $mapping = $this->classDiscriminatorResolver->getMappingForClass($class)) {
- if (!isset($data[$mapping->getTypeProperty()])) {
- throw NotNormalizableValueException::createForUnexpectedDataType(sprintf('Type property "%s" not found for the abstract object "%s".', $mapping->getTypeProperty(), $class), null, ['string'], isset($context['deserialization_path']) ? $context['deserialization_path'].'.'.$mapping->getTypeProperty() : $mapping->getTypeProperty(), false);
- }
-
- $type = $data[$mapping->getTypeProperty()];
- if (null === ($mappedClass = $mapping->getClassForType($type))) {
- throw NotNormalizableValueException::createForUnexpectedDataType(sprintf('The type "%s" is not a valid value.', $type), $type, ['string'], isset($context['deserialization_path']) ? $context['deserialization_path'].'.'.$mapping->getTypeProperty() : $mapping->getTypeProperty(), true);
- }
-
- if ($mappedClass !== $class) {
- return $this->instantiateObject($data, $mappedClass, $context, new \ReflectionClass($mappedClass), $allowedAttributes, $format);
- }
+ if ($class !== $mappedClass = $this->getMappedClass($data, $class, $context)) {
+ return $this->instantiateObject($data, $mappedClass, $context, new \ReflectionClass($mappedClass), $allowedAttributes, $format);
}
return parent::instantiateObject($data, $class, $context, $reflectionClass, $allowedAttributes, $format);
@@ -270,7 +259,7 @@ protected function getAttributes(object $object, ?string $format, array $context
$attributes = $this->extractAttributes($object, $format, $context);
- if ($this->classDiscriminatorResolver && $mapping = $this->classDiscriminatorResolver->getMappingForMappedObject($object)) {
+ if ($mapping = $this->classDiscriminatorResolver?->getMappingForMappedObject($object)) {
array_unshift($attributes, $mapping->getTypeProperty());
}
@@ -319,11 +308,9 @@ public function denormalize(mixed $data, string $type, string $format = null, ar
$normalizedData = $this->prepareForDenormalization($data);
$extraAttributes = [];
- $reflectionClass = new \ReflectionClass($type);
- $object = $this->instantiateObject($normalizedData, $type, $context, $reflectionClass, $allowedAttributes, $format);
- $resolvedClass = $this->objectClassResolver ? ($this->objectClassResolver)($object) : $object::class;
+ $mappedClass = $this->getMappedClass($normalizedData, $type, $context);
- $nestedAttributes = $this->getNestedAttributes($resolvedClass);
+ $nestedAttributes = $this->getNestedAttributes($mappedClass);
$nestedData = [];
$propertyAccessor = PropertyAccess::createPropertyAccessor();
foreach ($nestedAttributes as $property => $serializedPath) {
@@ -336,6 +323,9 @@ public function denormalize(mixed $data, string $type, string $format = null, ar
$normalizedData = array_merge($normalizedData, $nestedData);
+ $object = $this->instantiateObject($normalizedData, $mappedClass, $context, new \ReflectionClass($mappedClass), $allowedAttributes, $format);
+ $resolvedClass = $this->objectClassResolver ? ($this->objectClassResolver)($object) : $object::class;
+
foreach ($normalizedData as $attribute => $value) {
if ($this->nameConverter) {
$notConverted = $attribute;
@@ -675,11 +665,8 @@ private function updateData(array $data, string $attribute, mixed $attributeValu
*/
private function isMaxDepthReached(array $attributesMetadata, string $class, string $attribute, array &$context): bool
{
- $enableMaxDepth = $context[self::ENABLE_MAX_DEPTH] ?? $this->defaultContext[self::ENABLE_MAX_DEPTH] ?? false;
- if (
- !$enableMaxDepth ||
- !isset($attributesMetadata[$attribute]) ||
- null === $maxDepth = $attributesMetadata[$attribute]->getMaxDepth()
+ if (!($enableMaxDepth = $context[self::ENABLE_MAX_DEPTH] ?? $this->defaultContext[self::ENABLE_MAX_DEPTH] ?? false)
+ || null === $maxDepth = $attributesMetadata[$attribute]?->getMaxDepth()
) {
return false;
}
@@ -755,7 +742,7 @@ private function isUninitializedValueError(\Error $e): bool
*/
private function getNestedAttributes(string $class): array
{
- if (!$this->classMetadataFactory || !$this->classMetadataFactory->hasMetadataFor($class)) {
+ if (!$this->classMetadataFactory?->hasMetadataFor($class)) {
return [];
}
@@ -781,15 +768,30 @@ private function getNestedAttributes(string $class): array
private function removeNestedValue(array $path, array $data): array
{
$element = array_shift($path);
- if ([] === $path) {
+ if (!$path || !$data[$element] = $this->removeNestedValue($path, $data[$element])) {
unset($data[$element]);
- } else {
- $data[$element] = $this->removeNestedValue($path, $data[$element]);
- if ([] === $data[$element]) {
- unset($data[$element]);
- }
}
return $data;
}
+
+ /**
+ * @return class-string
+ */
+ private function getMappedClass(array $data, string $class, array $context): string
+ {
+ if (!$mapping = $this->classDiscriminatorResolver?->getMappingForClass($class)) {
+ return $class;
+ }
+
+ if (null === $type = $data[$mapping->getTypeProperty()] ?? null) {
+ throw NotNormalizableValueException::createForUnexpectedDataType(sprintf('Type property "%s" not found for the abstract object "%s".', $mapping->getTypeProperty(), $class), null, ['string'], isset($context['deserialization_path']) ? $context['deserialization_path'].'.'.$mapping->getTypeProperty() : $mapping->getTypeProperty(), false);
+ }
+
+ if (null === $mappedClass = $mapping->getClassForType($type)) {
+ throw NotNormalizableValueException::createForUnexpectedDataType(sprintf('The type "%s" is not a valid value.', $type), $type, ['string'], isset($context['deserialization_path']) ? $context['deserialization_path'].'.'.$mapping->getTypeProperty() : $mapping->getTypeProperty(), true);
+ }
+
+ return $mappedClass;
+ }
}
diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/Annotations/SerializedPathInConstructorDummy.php b/src/Symfony/Component/Serializer/Tests/Fixtures/Annotations/SerializedPathInConstructorDummy.php
new file mode 100644
index 0000000000000..a6d5109086899
--- /dev/null
+++ b/src/Symfony/Component/Serializer/Tests/Fixtures/Annotations/SerializedPathInConstructorDummy.php
@@ -0,0 +1,25 @@
+
+ *
+ * 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\Annotations;
+
+use Symfony\Component\Serializer\Annotation\SerializedPath;
+
+class SerializedPathInConstructorDummy
+{
+ public function __construct(
+ /**
+ * @SerializedPath("[one][two]")
+ */
+ public $three,
+ ) {
+ }
+}
diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/Attributes/SerializedPathInConstructorDummy.php b/src/Symfony/Component/Serializer/Tests/Fixtures/Attributes/SerializedPathInConstructorDummy.php
new file mode 100644
index 0000000000000..90aee115417d4
--- /dev/null
+++ b/src/Symfony/Component/Serializer/Tests/Fixtures/Attributes/SerializedPathInConstructorDummy.php
@@ -0,0 +1,22 @@
+
+ *
+ * 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\Attributes;
+
+use Symfony\Component\Serializer\Annotation\SerializedPath;
+
+class SerializedPathInConstructorDummy
+{
+ public function __construct(
+ #[SerializedPath('[one][two]')] public $three,
+ ) {
+ }
+}
diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/serialization.xml b/src/Symfony/Component/Serializer/Tests/Fixtures/serialization.xml
index 4890f56bfd0f9..5ca6ac412fc07 100644
--- a/src/Symfony/Component/Serializer/Tests/Fixtures/serialization.xml
+++ b/src/Symfony/Component/Serializer/Tests/Fixtures/serialization.xml
@@ -30,6 +30,10 @@
+
+
+
+
diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/serialization.yml b/src/Symfony/Component/Serializer/Tests/Fixtures/serialization.yml
index 7519b979efa96..e052d65a88779 100644
--- a/src/Symfony/Component/Serializer/Tests/Fixtures/serialization.yml
+++ b/src/Symfony/Component/Serializer/Tests/Fixtures/serialization.yml
@@ -22,6 +22,10 @@
serialized_path: '[one][two]'
seven:
serialized_path: '[three][four]'
+'Symfony\Component\Serializer\Tests\Fixtures\Annotations\SerializedPathInConstructorDummy':
+ attributes:
+ three:
+ serialized_path: '[one][two]'
'Symfony\Component\Serializer\Tests\Fixtures\Annotations\AbstractDummy':
discriminator_map:
type_property: type
diff --git a/src/Symfony/Component/Serializer/Tests/Mapping/Factory/ClassMetadataFactoryCompilerTest.php b/src/Symfony/Component/Serializer/Tests/Mapping/Factory/ClassMetadataFactoryCompilerTest.php
index 5ce1931ba0cab..903612c684c0d 100644
--- a/src/Symfony/Component/Serializer/Tests/Mapping/Factory/ClassMetadataFactoryCompilerTest.php
+++ b/src/Symfony/Component/Serializer/Tests/Mapping/Factory/ClassMetadataFactoryCompilerTest.php
@@ -19,6 +19,7 @@
use Symfony\Component\Serializer\Tests\Fixtures\Annotations\MaxDepthDummy;
use Symfony\Component\Serializer\Tests\Fixtures\Annotations\SerializedNameDummy;
use Symfony\Component\Serializer\Tests\Fixtures\Annotations\SerializedPathDummy;
+use Symfony\Component\Serializer\Tests\Fixtures\Annotations\SerializedPathInConstructorDummy;
use Symfony\Component\Serializer\Tests\Fixtures\Dummy;
final class ClassMetadataFactoryCompilerTest extends TestCase
@@ -46,18 +47,20 @@ public function testItDumpMetadata()
$maxDepthDummyMetadata = $classMetatadataFactory->getMetadataFor(MaxDepthDummy::class);
$serializedNameDummyMetadata = $classMetatadataFactory->getMetadataFor(SerializedNameDummy::class);
$serializedPathDummyMetadata = $classMetatadataFactory->getMetadataFor(SerializedPathDummy::class);
+ $serializedPathInConstructorDummyMetadata = $classMetatadataFactory->getMetadataFor(SerializedPathInConstructorDummy::class);
$code = (new ClassMetadataFactoryCompiler())->compile([
$dummyMetadata,
$maxDepthDummyMetadata,
$serializedNameDummyMetadata,
$serializedPathDummyMetadata,
+ $serializedPathInConstructorDummyMetadata,
]);
file_put_contents($this->dumpPath, $code);
$compiledMetadata = require $this->dumpPath;
- $this->assertCount(4, $compiledMetadata);
+ $this->assertCount(5, $compiledMetadata);
$this->assertArrayHasKey(Dummy::class, $compiledMetadata);
$this->assertEquals([
@@ -99,5 +102,13 @@ public function testItDumpMetadata()
],
null,
], $compiledMetadata[SerializedPathDummy::class]);
+
+ $this->assertArrayHasKey(SerializedPathInConstructorDummy::class, $compiledMetadata);
+ $this->assertEquals([
+ [
+ 'three' => [[], null, null, '[one][two]'],
+ ],
+ null,
+ ], $compiledMetadata[SerializedPathInConstructorDummy::class]);
}
}
diff --git a/src/Symfony/Component/Serializer/Tests/Mapping/Loader/AnnotationLoaderTestCase.php b/src/Symfony/Component/Serializer/Tests/Mapping/Loader/AnnotationLoaderTestCase.php
index de7accd844b32..2dbd03703a2ce 100644
--- a/src/Symfony/Component/Serializer/Tests/Mapping/Loader/AnnotationLoaderTestCase.php
+++ b/src/Symfony/Component/Serializer/Tests/Mapping/Loader/AnnotationLoaderTestCase.php
@@ -103,6 +103,15 @@ public function testLoadSerializedPath()
$this->assertEquals(new PropertyPath('[three][four]'), $attributesMetadata['seven']->getSerializedPath());
}
+ public function testLoadSerializedPathInConstructor()
+ {
+ $classMetadata = new ClassMetadata($this->getNamespace().'\SerializedPathInConstructorDummy');
+ $this->loader->loadClassMetadata($classMetadata);
+
+ $attributesMetadata = $classMetadata->getAttributesMetadata();
+ $this->assertEquals(new PropertyPath('[one][two]'), $attributesMetadata['three']->getSerializedPath());
+ }
+
public function testLoadClassMetadataAndMerge()
{
$classMetadata = new ClassMetadata($this->getNamespace().'\GroupDummy');
diff --git a/src/Symfony/Component/Serializer/Tests/Mapping/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/Serializer/Tests/Mapping/Loader/XmlFileLoaderTest.php
index b1e9ed7222636..202534f56fca5 100644
--- a/src/Symfony/Component/Serializer/Tests/Mapping/Loader/XmlFileLoaderTest.php
+++ b/src/Symfony/Component/Serializer/Tests/Mapping/Loader/XmlFileLoaderTest.php
@@ -94,6 +94,15 @@ public function testSerializedPath()
$this->assertEquals('[three][four]', $attributesMetadata['seven']->getSerializedPath());
}
+ public function testSerializedPathInConstructor()
+ {
+ $classMetadata = new ClassMetadata('Symfony\Component\Serializer\Tests\Fixtures\Annotations\SerializedPathInConstructorDummy');
+ $this->loader->loadClassMetadata($classMetadata);
+
+ $attributesMetadata = $classMetadata->getAttributesMetadata();
+ $this->assertEquals('[one][two]', $attributesMetadata['three']->getSerializedPath());
+ }
+
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 bbe0a99aeab89..dcfd2b4afac51 100644
--- a/src/Symfony/Component/Serializer/Tests/Mapping/Loader/YamlFileLoaderTest.php
+++ b/src/Symfony/Component/Serializer/Tests/Mapping/Loader/YamlFileLoaderTest.php
@@ -108,6 +108,15 @@ public function testSerializedPath()
$this->assertEquals(new PropertyPath('[three][four]'), $attributesMetadata['seven']->getSerializedPath());
}
+ public function testSerializedPathInConstructor()
+ {
+ $classMetadata = new ClassMetadata('Symfony\Component\Serializer\Tests\Fixtures\Annotations\SerializedPathInConstructorDummy');
+ $this->loader->loadClassMetadata($classMetadata);
+
+ $attributesMetadata = $classMetadata->getAttributesMetadata();
+ $this->assertEquals(new PropertyPath('[one][two]'), $attributesMetadata['three']->getSerializedPath());
+ }
+
public function testLoadDiscriminatorMap()
{
$classMetadata = new ClassMetadata(AbstractDummy::class);
diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php
index 0c342b64d8000..d876193c8a741 100644
--- a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php
+++ b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php
@@ -18,6 +18,7 @@
use Symfony\Component\PropertyInfo\PropertyInfoExtractor;
use Symfony\Component\PropertyInfo\Type;
use Symfony\Component\Serializer\Annotation\Context;
+use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
use Symfony\Component\Serializer\Annotation\SerializedName;
use Symfony\Component\Serializer\Annotation\SerializedPath;
use Symfony\Component\Serializer\Exception\ExtraAttributesException;
@@ -171,6 +172,53 @@ public function testDenormalizeWithNestedAttributesDuplicateKeys()
$normalizer->denormalize($data, DuplicateKeyNestedDummy::class, 'any');
}
+ public function testDenormalizeWithNestedAttributesInConstructor()
+ {
+ $normalizer = new AbstractObjectNormalizerWithMetadata();
+ $data = [
+ 'one' => [
+ 'two' => [
+ 'three' => 'foo',
+ ],
+ 'four' => 'quux',
+ ],
+ 'foo' => 'notfoo',
+ 'baz' => 'baz',
+ ];
+ $test = $normalizer->denormalize($data, NestedDummyWithConstructor::class, 'any');
+ $this->assertSame('foo', $test->foo);
+ $this->assertSame('quux', $test->quux);
+ $this->assertSame('notfoo', $test->notfoo);
+ $this->assertSame('baz', $test->baz);
+ }
+
+ public function testDenormalizeWithNestedAttributesInConstructorAndDiscriminatorMap()
+ {
+ $normalizer = new AbstractObjectNormalizerWithMetadata();
+ $data = [
+ 'one' => [
+ 'two' => [
+ 'three' => 'foo',
+ ],
+ 'four' => 'quux',
+ ],
+ 'foo' => 'notfoo',
+ 'baz' => 'baz',
+ ];
+
+ $test1 = $normalizer->denormalize($data + ['type' => 'first'], AbstractNestedDummyWithConstructorAndDiscriminator::class, 'any');
+ $this->assertInstanceOf(FirstNestedDummyWithConstructorAndDiscriminator::class, $test1);
+ $this->assertSame('foo', $test1->foo);
+ $this->assertSame('notfoo', $test1->notfoo);
+ $this->assertSame('baz', $test1->baz);
+
+ $test2 = $normalizer->denormalize($data + ['type' => 'second'], AbstractNestedDummyWithConstructorAndDiscriminator::class, 'any');
+ $this->assertInstanceOf(SecondNestedDummyWithConstructorAndDiscriminator::class, $test2);
+ $this->assertSame('quux', $test2->quux);
+ $this->assertSame('notfoo', $test2->notfoo);
+ $this->assertSame('baz', $test2->baz);
+ }
+
public function testNormalizeWithNestedAttributesMixingArrayTypes()
{
$this->expectException(LogicException::class);
@@ -236,6 +284,52 @@ public function testNormalizeWithNestedAttributesWithoutMetadata()
$this->assertSame($data, $test);
}
+ public function testNormalizeWithNestedAttributesInConstructor()
+ {
+ $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
+ $normalizer = new ObjectNormalizer($classMetadataFactory, new MetadataAwareNameConverter($classMetadataFactory));
+
+ $test = $normalizer->normalize(new NestedDummyWithConstructor('foo', 'quux', 'notfoo', 'baz'), 'any');
+ $this->assertSame([
+ 'one' => [
+ 'two' => [
+ 'three' => 'foo',
+ ],
+ 'four' => 'quux',
+ ],
+ 'foo' => 'notfoo',
+ 'baz' => 'baz',
+ ], $test);
+ }
+
+ public function testNormalizeWithNestedAttributesInConstructorAndDiscriminatorMap()
+ {
+ $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
+ $normalizer = new ObjectNormalizer($classMetadataFactory, new MetadataAwareNameConverter($classMetadataFactory));
+
+ $test1 = $normalizer->normalize(new FirstNestedDummyWithConstructorAndDiscriminator('foo', 'notfoo', 'baz'), 'any');
+ $this->assertSame([
+ 'type' => 'first',
+ 'one' => [
+ 'two' => [
+ 'three' => 'foo',
+ ],
+ ],
+ 'foo' => 'notfoo',
+ 'baz' => 'baz',
+ ], $test1);
+
+ $test2 = $normalizer->normalize(new SecondNestedDummyWithConstructorAndDiscriminator('quux', 'notfoo', 'baz'), 'any');
+ $this->assertSame([
+ 'type' => 'second',
+ 'one' => [
+ 'four' => 'quux',
+ ],
+ 'foo' => 'notfoo',
+ 'baz' => 'baz',
+ ], $test2);
+ }
+
public function testDenormalizeCollectionDecodedFromXmlWithOneChild()
{
$denormalizer = $this->getDenormalizerForDummyCollection();
@@ -661,6 +755,78 @@ class NestedDummy
public $baz;
}
+class NestedDummyWithConstructor
+{
+ public function __construct(
+ /**
+ * @SerializedPath("[one][two][three]")
+ */
+ public $foo,
+
+ /**
+ * @SerializedPath("[one][four]")
+ */
+ public $quux,
+
+ /**
+ * @SerializedPath("[foo]")
+ */
+ public $notfoo,
+
+ public $baz,
+ ) {
+ }
+}
+
+/**
+ * @DiscriminatorMap(typeProperty="type", mapping={
+ * "first" = FirstNestedDummyWithConstructorAndDiscriminator::class,
+ * "second" = SecondNestedDummyWithConstructorAndDiscriminator::class,
+ * })
+ */
+abstract class AbstractNestedDummyWithConstructorAndDiscriminator
+{
+ public function __construct(
+ /**
+ * @SerializedPath("[foo]")
+ */
+ public $notfoo,
+
+ public $baz,
+ ) {
+ }
+}
+
+class FirstNestedDummyWithConstructorAndDiscriminator extends AbstractNestedDummyWithConstructorAndDiscriminator
+{
+ public function __construct(
+ /**
+ * @SerializedPath("[one][two][three]")
+ */
+ public $foo,
+
+ $notfoo,
+ $baz,
+ ) {
+ parent::__construct($notfoo, $baz);
+ }
+}
+
+class SecondNestedDummyWithConstructorAndDiscriminator extends AbstractNestedDummyWithConstructorAndDiscriminator
+{
+ public function __construct(
+ /**
+ * @SerializedPath("[one][four]")
+ */
+ public $quux,
+
+ $notfoo,
+ $baz,
+ ) {
+ parent::__construct($notfoo, $baz);
+ }
+}
+
class DuplicateKeyNestedDummy
{
/**
@@ -711,7 +877,9 @@ protected function getAttributeValue(object $object, string $attribute, string $
protected function setAttributeValue(object $object, string $attribute, $value, string $format = null, array $context = [])
{
- $object->$attribute = $value;
+ if (property_exists($object, $attribute)) {
+ $object->$attribute = $value;
+ }
}
}