Skip to content

SerializedName based on Groups #37903

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

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\LegacyBundle\Entity\LegacyPerson:
attributes:
name:
serialized_name: 'full_name'
serialized_names: 'full_name'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems to break user configs, thus BC

IMHO we can keep supporting 2 formats:

ser_name: 'some'
ser_names:
  some: ~
  other: [grp1, grp2]

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\ModernBundle\src\Entity\ModernPerson:
attributes:
name:
serialized_name: 'full_name'
serialized_names: 'full_name'
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ final class SerializedName
*/
private $serializedName;

private $groups = [];

public function __construct(array $data)
{
if (!isset($data['value'])) {
Expand All @@ -39,10 +41,16 @@ public function __construct(array $data)
}

$this->serializedName = $data['value'];
$this->groups = (array) ($data['groups'] ?? []);
}

public function getSerializedName(): string
{
return $this->serializedName;
}

public function getGroups(): array
{
return $this->groups;
}
}
54 changes: 44 additions & 10 deletions src/Symfony/Component/Serializer/Mapping/AttributeMetadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,11 @@ 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.
* {@link getSerializedNames()} instead.
*/
public $serializedName;
public $serializedNames = [];

/**
* @var bool
Expand Down Expand Up @@ -111,15 +109,51 @@ public function getMaxDepth()
*/
public function setSerializedName(string $serializedName = null)
{
$this->serializedName = $serializedName;
$this->addSerializedName($serializedName);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you cannot pass null here.

}

/**
* {@inheritdoc}
*/
public function addSerializedName(string $serializedName, array $groups = [])
{
$this->serializedNames[$serializedName] = $groups;
}

/**
* {@inheritdoc}
*/
public function getSerializedName(): ?string
{
return $this->serializedName;
return $this->getSerializedNameForGroups();
}

/**
* {@inheritdoc}
*/
public function getSerializedNames(): array
{
return $this->serializedNames;
}

/**
* {@inheritdoc}
*/
public function getSerializedNameForGroups(array $groups = []): ?string
{
$defaultSerializedName = null;

foreach ($this->serializedNames as $serializedName => $groupsForSerializedName) {
if (!$groupsForSerializedName) {
$defaultSerializedName = $serializedName;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i dont think you can make this assumption :/ any name without groups could be considered default

}

if (array_intersect($groups, $groupsForSerializedName)) {
return $serializedName;
}
}

return $defaultSerializedName;
}

/**
Expand Down Expand Up @@ -152,9 +186,9 @@ public function merge(AttributeMetadataInterface $attributeMetadata)
$this->maxDepth = $attributeMetadata->getMaxDepth();
}

// Overwrite only if not defined
if (null === $this->serializedName) {
$this->serializedName = $attributeMetadata->getSerializedName();
// Overwrite only if empty or nullable array
if (!$this->serializedNames) {
$this->serializedNames = $attributeMetadata->getSerializedNames();
}

if ($ignore = $attributeMetadata->isIgnored()) {
Expand All @@ -169,6 +203,6 @@ public function merge(AttributeMetadataInterface $attributeMetadata)
*/
public function __sleep()
{
return ['name', 'groups', 'maxDepth', 'serializedName', 'ignore'];
return ['name', 'groups', 'maxDepth', 'serializedNames', 'ignore'];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,26 @@ public function getMaxDepth();
*/
public function setSerializedName(string $serializedName = null);

/**
* Adds the serialization name for this attribute.
*/
public function addSerializedName(string $serializedName, array $groups = []);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMHO should this one should be setSerializedNames (no need for an adder) - which simplifies resetting

setSerializedName(null) previous, vs setSerializedNames([]) now


/**
* Gets the serialization name for this attribute.
*/
public function getSerializedName(): ?string;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

id remove/deprecate this one


/**
* Gets the serialization names for this attribute.
*/
public function getSerializedNames(): array;

/**
* Gets the serialization name for this attribute.
*/
public function getSerializedNameForGroups(array $groups = []): ?string;

/**
* Sets if this attribute must be ignored or not.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ private function generateDeclaredClassMetadata(array $classMetadatas): string
$attributesMetadata[$attributeMetadata->getName()] = [
$attributeMetadata->getGroups(),
$attributeMetadata->getMaxDepth(),
$attributeMetadata->getSerializedName(),
$attributeMetadata->getSerializedNames(),
];
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,9 @@ public function __construct(string $compiledClassMetadataFile, ClassMetadataFact
}

$compiledClassMetadata = require $compiledClassMetadataFile;

if (!\is_array($compiledClassMetadata)) {
throw new \RuntimeException(sprintf('Compiled metadata must be of the type array, %s given.', \gettype($compiledClassMetadata)));
throw new \RuntimeException(sprintf('Compiled metadata must be of the type array, "%s" given.', \gettype($compiledClassMetadata)));
}

$this->compiledClassMetadata = $compiledClassMetadata;
Expand All @@ -56,7 +57,7 @@ public function getMetadataFor($value)
$classMetadata = new ClassMetadata($className);
foreach ($this->compiledClassMetadata[$className][0] as $name => $compiledAttributesMetadata) {
$classMetadata->attributesMetadata[$name] = $attributeMetadata = new AttributeMetadata($name);
[$attributeMetadata->groups, $attributeMetadata->maxDepth, $attributeMetadata->serializedName] = $compiledAttributesMetadata;
[$attributeMetadata->groups, $attributeMetadata->maxDepth, $attributeMetadata->serializedNames] = $compiledAttributesMetadata;
}
$classMetadata->classDiscriminatorMapping = $this->compiledClassMetadata[$className][1]
? new ClassDiscriminatorMapping(...$this->compiledClassMetadata[$className][1])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ 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());
$attributesMetadata[$property->name]->addSerializedName($annotation->getSerializedName(), $annotation->getGroups());
} elseif ($annotation instanceof Ignore) {
$attributesMetadata[$property->name]->setIgnore(true);
}
Expand Down Expand Up @@ -117,8 +117,7 @@ public function loadClassMetadata(ClassMetadataInterface $classMetadata)
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());
$attributeMetadata->addSerializedName($annotation->getSerializedName(), $annotation->getGroups());
} elseif ($annotation instanceof Ignore) {
$attributeMetadata->setIgnore(true);
}
Expand Down
17 changes: 7 additions & 10 deletions src/Symfony/Component/Serializer/Mapping/Loader/XmlFileLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,28 +52,25 @@ public function loadClassMetadata(ClassMetadataInterface $classMetadata)
foreach ($xml->attribute as $attribute) {
$attributeName = (string) $attribute['name'];

if (isset($attributesMetadata[$attributeName])) {
$attributeMetadata = $attributesMetadata[$attributeName];
} else {
$attributeMetadata = new AttributeMetadata($attributeName);
$classMetadata->addAttributeMetadata($attributeMetadata);
}
$attributesMetadata[$attributeName] = $attributesMetadata[$attributeName] ?? new AttributeMetadata($attributeName);

foreach ($attribute->group as $group) {
$attributeMetadata->addGroup((string) $group);
$attributesMetadata[$attributeName]->addGroup((string) $group);
}

if (isset($attribute['max-depth'])) {
$attributeMetadata->setMaxDepth((int) $attribute['max-depth']);
$attributesMetadata[$attributeName]->setMaxDepth((int) $attribute['max-depth']);
}

if (isset($attribute['serialized-name'])) {
$attributeMetadata->setSerializedName((string) $attribute['serialized-name']);
$attributesMetadata[$attributeName]->addSerializedName((string) $attribute['serialized-name'], (array) $attribute->group);
}

if (isset($attribute['ignore'])) {
$attributeMetadata->setIgnore((bool) $attribute['ignore']);
$attributesMetadata[$attributeName]->setIgnore((bool) $attribute['ignore']);
}

$classMetadata->addAttributeMetadata($attributesMetadata[$attributeName]);
}

if (isset($xml->{'discriminator-map'})) {
Expand Down
24 changes: 20 additions & 4 deletions src/Symfony/Component/Serializer/Mapping/Loader/YamlFileLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,28 @@ 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()));
if (isset($data['serialized_names'])) {
if (!\is_string($data['serialized_names']) && !\is_array($data['serialized_names'])) {
throw new MappingException(sprintf('The "serialized_names" value must be a non-empty string or an array of serialized name/groups in "%s" for the attribute "%s" of the class "%s".', $this->file, $attribute, $classMetadata->getName()));
}

$attributeMetadata->setSerializedName($data['serialized_name']);
if (\is_string($data['serialized_names'])) {
if (!$data['serialized_names']) {
throw new MappingException(sprintf('The "serialized_names" value must be a non-empty string in "%s" for the attribute "%s" of the class "%s".', $this->file, $attribute, $classMetadata->getName()));
}
$attributeMetadata->addSerializedName($data['serialized_names']);
} elseif (\is_array($data['serialized_names'])) {
if (empty($data['serialized_names'])) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (!$data['serialized_names']) {

throw new MappingException(sprintf('The "serialized_names" value must be a non-empty array of serialized name/groups in "%s" for the attribute "%s" of the class "%s".', $this->file, $attribute, $classMetadata->getName()));
}
foreach ($data['serialized_names'] as $serializedName => $groups) {
if (!\is_string($serializedName) || empty($serializedName)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

|| '' === trim($serializedName)

throw new MappingException(sprintf('The key for "serialized_names" array must be a non-empty string in "%s" for the attribute "%s" of the class "%s".', $this->file, $attribute, $classMetadata->getName()));
}

$attributeMetadata->addSerializedName($serializedName, (array) $groups);
}
}
}

if (isset($data['ignore'])) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public function normalize(string $propertyName, string $class = null, string $fo
}

if (!\array_key_exists($class, self::$normalizeCache) || !\array_key_exists($propertyName, self::$normalizeCache[$class])) {
self::$normalizeCache[$class][$propertyName] = $this->getCacheValueForNormalization($propertyName, $class);
self::$normalizeCache[$class][$propertyName] = $this->getCacheValueForNormalization($propertyName, $class, $context);
}

return self::$normalizeCache[$class][$propertyName] ?? $this->normalizeFallback($propertyName, $class, $format, $context);
Expand All @@ -71,7 +71,7 @@ public function denormalize(string $propertyName, string $class = null, string $
return self::$denormalizeCache[$cacheKey][$propertyName] ?? $this->denormalizeFallback($propertyName, $class, $format, $context);
}

private function getCacheValueForNormalization(string $propertyName, string $class): ?string
private function getCacheValueForNormalization(string $propertyName, string $class, array $context = []): ?string
{
if (!$this->metadataFactory->hasMetadataFor($class)) {
return null;
Expand All @@ -82,7 +82,7 @@ private function getCacheValueForNormalization(string $propertyName, string $cla
return null;
}

return $attributesMetadata[$propertyName]->getSerializedName() ?? null;
return $attributesMetadata[$propertyName]->getSerializedNameForGroups((array) ($context['groups'] ?? []));
}

private function normalizeFallback(string $propertyName, string $class = null, string $format = null, array $context = []): string
Expand Down Expand Up @@ -115,7 +115,7 @@ private function getCacheValueForAttributesMetadata(string $class, array $contex

$cache = [];
foreach ($classMetadata->getAttributesMetadata() as $name => $metadata) {
if (null === $metadata->getSerializedName()) {
if (null === $metadata->getSerializedNameForGroups((array) ($context['groups'] ?? []))) {
continue;
}

Expand All @@ -127,7 +127,7 @@ private function getCacheValueForAttributesMetadata(string $class, array $contex
continue;
}

$cache[$metadata->getSerializedName()] = $name;
$cache[$metadata->getSerializedNameForGroups($groups)] = $name;
}

return $cache;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,11 @@ public function testSerializedNameParameters()
$maxDepth = new SerializedName(['value' => 'foo']);
$this->assertEquals('foo', $maxDepth->getSerializedName());
}

public function testSerializedNameParametersWithGroups()
{
$maxDepth = new SerializedName(['value' => 'foo', 'groups' => ['group1', 'group2']]);
$this->assertEquals('foo', $maxDepth->getSerializedName());
$this->assertEquals(['group1', 'group2'], $maxDepth->getGroups());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class AbstractNormalizerDummy extends AbstractNormalizer
/**
* {@inheritdoc}
*/
public function getAllowedAttributes($classOrObject, array $context, bool $attributesAsString = false)
public function getAllowedAttributes($classOrObject, array $context, bool $attributesAsString = false)
{
return parent::getAllowedAttributes($classOrObject, $context, $attributesAsString);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* 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\Groups;
use Symfony\Component\Serializer\Annotation\SerializedName;

/**
* @author Jordan Samouh <jordan.samouh@gmail.com>
*/
class SerializedNameWithGroupsDummy
{
/**
* @SerializedName("baz")
*/
public $foo;

public $bar;

public $quux;

/**
* @SerializedName("bazs")
*/
public $foos;

/**
* @SerializedName("bargroups", groups={"group1"})
* @Groups({"group1"})
*/
public $barWithGroup;

/**
* @SerializedName("quuxgroups2", groups={"group1", "group2"})
* @SerializedName("quuxgroups1", groups={"group1"})
* @Groups({"group1", "group2"})
*/
public $quuxWithGroups;

/**
* @SerializedName("qux")
* @Groups({"group1"})
*/
public function getBar()
{
return $this->bar;
}
}
Loading