diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0f25072..bf64357 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,11 @@ CHANGELOG
-----
* Added support of OpenAPI 3.0. The internals were completely reworked and this version introduces BC breaks.
+3.7.0
+-----
+
+* Added `@SerializedName` annotation support and name converters when using Symfony >= 4.2.
+
3.3.0
-----
diff --git a/ModelDescriber/ObjectModelDescriber.php b/ModelDescriber/ObjectModelDescriber.php
index 3c33385..4a26100 100644
--- a/ModelDescriber/ObjectModelDescriber.php
+++ b/ModelDescriber/ObjectModelDescriber.php
@@ -21,6 +21,7 @@ use Nelmio\ApiDocBundle\PropertyDescriber\PropertyDescriberInterface;
use OpenApi\Annotations as OA;
use Symfony\Component\PropertyInfo\PropertyInfoExtractorInterface;
use Symfony\Component\PropertyInfo\Type;
+use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
class ObjectModelDescriber implements ModelDescriberInterface, ModelRegistryAwareInterface
{
@@ -34,6 +35,8 @@ class ObjectModelDescriber implements ModelDescriberInterface, ModelRegistryAwar
private $propertyDescribers;
/** @var string[] */
private $mediaTypes;
+ /** @var NameConverterInterface[] */
+ private $nameConverter;
private $swaggerDefinitionAnnotationReader;
@@ -41,12 +44,14 @@ class ObjectModelDescriber implements ModelDescriberInterface, ModelRegistryAwar
PropertyInfoExtractorInterface $propertyInfo,
Reader $reader,
$propertyDescribers,
- array $mediaTypes
+ array $mediaTypes,
+ NameConverterInterface $nameConverter = null
) {
$this->propertyInfo = $propertyInfo;
$this->doctrineReader = $reader;
$this->propertyDescribers = $propertyDescribers;
$this->mediaTypes = $mediaTypes;
+ $this->nameConverter = $nameConverter;
}
public function describe(Model $model, OA\Schema $schema)
@@ -70,10 +75,12 @@ class ObjectModelDescriber implements ModelDescriberInterface, ModelRegistryAwar
}
foreach ($propertyInfoProperties as $propertyName) {
+ $serializedName = null !== $this->nameConverter ? $this->nameConverter->normalize($propertyName, $class, null, null !== $model->getGroups() ? ['groups' => $model->getGroups()] : []) : $propertyName;
+
// read property options from OpenApi Property annotation if it exists
if (property_exists($class, $propertyName)) {
$reflectionProperty = new \ReflectionProperty($class, $propertyName);
- $property = Util::getProperty($schema, $annotationsReader->getPropertyName($reflectionProperty, $propertyName));
+ $property = Util::getProperty($schema, $annotationsReader->getPropertyName($reflectionProperty, $serializedName));
$groups = $model->getGroups();
if (isset($groups[$propertyName]) && is_array($groups[$propertyName])) {
@@ -82,7 +89,7 @@ class ObjectModelDescriber implements ModelDescriberInterface, ModelRegistryAwar
$annotationsReader->updateProperty($reflectionProperty, $property, $groups);
} else {
- $property = Util::getProperty($schema, $propertyName);
+ $property = Util::getProperty($schema, $serializedName);
}
// If type manually defined
diff --git a/Resources/config/services.xml b/Resources/config/services.xml
index a511711..91cda00 100644
--- a/Resources/config/services.xml
+++ b/Resources/config/services.xml
@@ -43,6 +43,7 @@
+
diff --git a/Tests/Functional/Controller/SerializedNameController.php b/Tests/Functional/Controller/SerializedNameController.php
new file mode 100644
index 0000000..736a266
--- /dev/null
+++ b/Tests/Functional/Controller/SerializedNameController.php
@@ -0,0 +1,37 @@
+= 4.2).
+ *
+ * @Route("/api", host="api.example.com")
+ */
+class SerializedNameController
+{
+ /**
+ * @OA\Response(
+ * response="200",
+ * description="success",
+ * @Model(type=SerializedNameEnt::class)
+ * )
+ * @Route("/serializename", methods={"GET"})
+ */
+ public function serializedNameAction()
+ {
+ }
+}
diff --git a/Tests/Functional/EntityExcluded/SerializedNameEnt.php b/Tests/Functional/EntityExcluded/SerializedNameEnt.php
new file mode 100644
index 0000000..a7dc459
--- /dev/null
+++ b/Tests/Functional/EntityExcluded/SerializedNameEnt.php
@@ -0,0 +1,36 @@
+
+ */
+class SerializedNameEnt
+{
+ /**
+ * @SerializedName("notfoo")
+ *
+ * @var string
+ */
+ public $foo;
+
+ /**
+ * Tests serialized name feature.
+ *
+ * @SerializedName("notwhatyouthink")
+ */
+ public function setBar(string $bar)
+ {
+ }
+}
diff --git a/Tests/Functional/FunctionalTest.php b/Tests/Functional/FunctionalTest.php
index 263fd50..6e9ea49 100644
--- a/Tests/Functional/FunctionalTest.php
+++ b/Tests/Functional/FunctionalTest.php
@@ -13,6 +13,7 @@ namespace Nelmio\ApiDocBundle\Tests\Functional;
use Nelmio\ApiDocBundle\OpenApiPhp\Util;
use OpenApi\Annotations as OA;
+use Symfony\Component\Serializer\Annotation\SerializedName;
class FunctionalTest extends WebTestCase
{
@@ -409,4 +410,20 @@ class FunctionalTest extends WebTestCase
{
$this->assertNotHasParameter('name', 'path', $this->getOperation('/api/article/{id}', 'get'));
}
+
+ public function testSerializedNameAction()
+ {
+ if (!class_exists(SerializedName::class)) {
+ $this->markTestSkipped('Annotation @SerializedName doesn\'t exist.');
+ }
+
+ $model = $this->getModel('SerializedNameEnt');
+ $this->assertCount(2, $model->properties);
+
+ $this->assertNotHasProperty('foo', $model);
+ $this->assertHasProperty('notfoo', $model);
+
+ $this->assertNotHasProperty('bar', $model);
+ $this->assertHasProperty('notwhatyouthink', $model);
+ }
}
diff --git a/Tests/Functional/TestKernel.php b/Tests/Functional/TestKernel.php
index 7643f0c..993d46b 100644
--- a/Tests/Functional/TestKernel.php
+++ b/Tests/Functional/TestKernel.php
@@ -29,6 +29,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\Routing\RouteCollectionBuilder;
+use Symfony\Component\Serializer\Annotation\SerializedName;
class TestKernel extends Kernel
{
@@ -87,6 +88,10 @@ class TestKernel extends Kernel
$routes->add('/docs/{area}', 'nelmio_api_doc.controller.swagger_ui')->setDefault('area', 'default');
$routes->add('/docs.json', 'nelmio_api_doc.controller.swagger');
+ if (class_exists(SerializedName::class)) {
+ $routes->import(__DIR__.'/Controller/SerializedNameController.php', '/', 'annotation');
+ }
+
if (class_exists(FOSRestBundle::class)) {
$routes->import(__DIR__.'/Controller/FOSRestController.php', '/', 'annotation');
}