Skip to content

Commit

Permalink
Merge pull request #982 from supersmile2009/bugfix/discriminator-base…
Browse files Browse the repository at this point in the history
…-class-v1

Discriminator property serialization when parent is in discriminator map for v1
  • Loading branch information
goetas authored Sep 12, 2018
2 parents abeba66 + de10005 commit 3888192
Show file tree
Hide file tree
Showing 18 changed files with 214 additions and 34 deletions.
84 changes: 50 additions & 34 deletions src/JMS/Serializer/Metadata/ClassMetadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ public function setDiscriminator($fieldName, array $map, array $groups = array()
$this->discriminatorFieldName = $fieldName;
$this->discriminatorMap = $map;
$this->discriminatorGroups = $groups;

$this->handleDiscriminatorProperty();
}

/**
Expand Down Expand Up @@ -167,40 +169,7 @@ public function merge(MergeableInterface $object)
$this->discriminatorBaseClass = $object->discriminatorBaseClass;
}

if ($this->discriminatorMap && !$this->reflection->isAbstract()) {
if (false === $typeValue = array_search($this->name, $this->discriminatorMap, true)) {
throw new \LogicException(sprintf(
'The sub-class "%s" is not listed in the discriminator of the base class "%s".',
$this->name,
$this->discriminatorBaseClass
));
}

$this->discriminatorValue = $typeValue;

if (isset($this->propertyMetadata[$this->discriminatorFieldName])
&& !$this->propertyMetadata[$this->discriminatorFieldName] instanceof StaticPropertyMetadata
) {
throw new \LogicException(sprintf(
'The discriminator field name "%s" of the base-class "%s" conflicts with a regular property of the sub-class "%s".',
$this->discriminatorFieldName,
$this->discriminatorBaseClass,
$this->name
));
}

$discriminatorProperty = new StaticPropertyMetadata(
$this->name,
$this->discriminatorFieldName,
$typeValue,
$this->discriminatorGroups
);
$discriminatorProperty->serializedName = $this->discriminatorFieldName;
$discriminatorProperty->xmlAttribute = $this->xmlDiscriminatorAttribute;
$discriminatorProperty->xmlElementCData = $this->xmlDiscriminatorCData;
$discriminatorProperty->xmlNamespace = $this->xmlDiscriminatorNamespace;
$this->propertyMetadata[$this->discriminatorFieldName] = $discriminatorProperty;
}
$this->handleDiscriminatorProperty();

$this->sortProperties();
}
Expand Down Expand Up @@ -297,6 +266,53 @@ public function unserialize($str)
parent::unserialize($parentStr);
}

private function handleDiscriminatorProperty()
{
if ($this->discriminatorMap && !$this->reflection->isAbstract() && !$this->reflection->isInterface()) {
if (false === $typeValue = array_search($this->name, $this->discriminatorMap, true)) {
if ($this->discriminatorBaseClass === $this->name) {
@trigger_error(
'Discriminator map was configured on non-abstract parent class but parent class'
.' was not included into discriminator map. It will throw exception in next major version.'
.' Either declare parent as abstract class or add it into discriminator map.',
E_USER_DEPRECATED
);
} else {
throw new \LogicException(sprintf(
'The sub-class "%s" is not listed in the discriminator of the base class "%s".',
$this->name,
$this->discriminatorBaseClass
));
}
}

$this->discriminatorValue = $typeValue;

if (isset($this->propertyMetadata[$this->discriminatorFieldName])
&& !$this->propertyMetadata[$this->discriminatorFieldName] instanceof StaticPropertyMetadata
) {
throw new \LogicException(sprintf(
'The discriminator field name "%s" of the base-class "%s" conflicts with a regular property of the sub-class "%s".',
$this->discriminatorFieldName,
$this->discriminatorBaseClass,
$this->name
));
}

$discriminatorProperty = new StaticPropertyMetadata(
$this->name,
$this->discriminatorFieldName,
$typeValue,
$this->discriminatorGroups
);
$discriminatorProperty->serializedName = $this->discriminatorFieldName;
$discriminatorProperty->xmlAttribute = $this->xmlDiscriminatorAttribute;
$discriminatorProperty->xmlElementCData = $this->xmlDiscriminatorCData;
$discriminatorProperty->xmlNamespace = $this->xmlDiscriminatorNamespace;
$this->propertyMetadata[$this->discriminatorFieldName] = $discriminatorProperty;
}
}

private function sortProperties()
{
switch ($this->accessorOrder) {
Expand Down
7 changes: 7 additions & 0 deletions tests/Fixtures/Discriminator/ImagePost.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace JMS\Serializer\Tests\Fixtures\Discriminator;

class ImagePost extends Post
{
}
22 changes: 22 additions & 0 deletions tests/Fixtures/Discriminator/Post.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

namespace JMS\Serializer\Tests\Fixtures\Discriminator;

use JMS\Serializer\Annotation as Serializer;

/**
* @Serializer\Discriminator(field = "type", map = {
* "post": "JMS\Serializer\Tests\Fixtures\Discriminator\Post",
* "image_post": "JMS\Serializer\Tests\Fixtures\Discriminator\ImagePost",
* })
*/
class Post
{
/** @Serializer\Type("string") */
public $title;

public function __construct($title)
{
$this->title = $title;
}
}
29 changes: 29 additions & 0 deletions tests/Metadata/Driver/BaseDriverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,23 @@ public function testLoadDiscriminator()
);
}

public function testLoadDiscriminatorWhenParentIsInDiscriminatorMap()
{
/** @var ClassMetadata $m */
$m = $this->getDriver()->loadMetadataForClass(new \ReflectionClass('JMS\Serializer\Tests\Fixtures\Discriminator\Post'));

self::assertNotNull($m);
self::assertEquals('type', $m->discriminatorFieldName);
self::assertEquals($m->name, $m->discriminatorBaseClass);
self::assertEquals(
[
'post' => 'JMS\Serializer\Tests\Fixtures\Discriminator\Post',
'image_post' => 'JMS\Serializer\Tests\Fixtures\Discriminator\ImagePost',
],
$m->discriminatorMap
);
}

public function testLoadXmlDiscriminator()
{
/** @var $m ClassMetadata */
Expand Down Expand Up @@ -281,6 +298,18 @@ public function testLoadDiscriminatorSubClass()
$this->assertEquals(array(), $m->discriminatorMap);
}

public function testLoadDiscriminatorSubClassWhenParentIsInDiscriminatorMap()
{
/** @var ClassMetadata $m */
$m = $this->getDriver()->loadMetadataForClass(new \ReflectionClass('JMS\Serializer\Tests\Fixtures\Discriminator\ImagePost'));

self::assertNotNull($m);
self::assertNull($m->discriminatorValue);
self::assertNull($m->discriminatorBaseClass);
self::assertNull($m->discriminatorFieldName);
self::assertEquals([], $m->discriminatorMap);
}

public function testLoadXmlObjectWithNamespacesMetadata()
{
$m = $this->getDriver()->loadMetadataForClass(new \ReflectionClass('JMS\Serializer\Tests\Fixtures\ObjectWithXmlNamespaces'));
Expand Down
7 changes: 7 additions & 0 deletions tests/Metadata/Driver/php/Discriminator.ImagePost.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

use JMS\Serializer\Metadata\ClassMetadata;

$metadata = new ClassMetadata('JMS\Serializer\Tests\Fixtures\Discriminator\ImagePost');

return $metadata;
16 changes: 16 additions & 0 deletions tests/Metadata/Driver/php/Discriminator.Post.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

use JMS\Serializer\Metadata\ClassMetadata;
use JMS\Serializer\Metadata\PropertyMetadata;

$metadata = new ClassMetadata('JMS\Serializer\Tests\Fixtures\Discriminator\Post');
$metadata->setDiscriminator('type', array(
'post' => 'JMS\Serializer\Tests\Fixtures\Discriminator\Post',
'image_post' => 'JMS\Serializer\Tests\Fixtures\Discriminator\ImagePost',
));

$title = new PropertyMetadata('JMS\Serializer\Tests\Fixtures\Discriminator\Post', 'title');
$title->setType('string');
$metadata->addPropertyMetadata($title);

return $metadata;
5 changes: 5 additions & 0 deletions tests/Metadata/Driver/xml/Discriminator.ImagePost.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<serializer>
<class name="JMS\Serializer\Tests\Fixtures\Discriminator\ImagePost">
</class>
</serializer>
8 changes: 8 additions & 0 deletions tests/Metadata/Driver/xml/Discriminator.Post.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<serializer>
<class name="JMS\Serializer\Tests\Fixtures\Discriminator\Post" discriminator-field-name="type">
<discriminator-class value="post">JMS\Serializer\Tests\Fixtures\Discriminator\Post</discriminator-class>
<discriminator-class value="image_post">JMS\Serializer\Tests\Fixtures\Discriminator\ImagePost</discriminator-class>
<property name="title" type="string" />
</class>
</serializer>
1 change: 1 addition & 0 deletions tests/Metadata/Driver/yml/Discriminator.ImagePost.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
JMS\Serializer\Tests\Fixtures\Discriminator\ImagePost: { }
10 changes: 10 additions & 0 deletions tests/Metadata/Driver/yml/Discriminator.Post.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
JMS\Serializer\Tests\Fixtures\Discriminator\Post:
discriminator:
field_name: type
map:
post: JMS\Serializer\Tests\Fixtures\Discriminator\Post
image_post: JMS\Serializer\Tests\Fixtures\Discriminator\ImagePost

properties:
title:
type: string
37 changes: 37 additions & 0 deletions tests/Serializer/BaseSerializationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@
use JMS\Serializer\Tests\Fixtures\CustomDeserializationObject;
use JMS\Serializer\Tests\Fixtures\DateTimeArraysObject;
use JMS\Serializer\Tests\Fixtures\Discriminator\Car;
use JMS\Serializer\Tests\Fixtures\Discriminator\ImagePost;
use JMS\Serializer\Tests\Fixtures\Discriminator\Moped;
use JMS\Serializer\Tests\Fixtures\Discriminator\Post;
use JMS\Serializer\Tests\Fixtures\Garage;
use JMS\Serializer\Tests\Fixtures\GetSetObject;
use JMS\Serializer\Tests\Fixtures\GroupsObject;
Expand Down Expand Up @@ -1237,6 +1239,14 @@ public function testPolymorphicObjects()
$this->getContent('car'),
$this->serialize(new Car(5))
);
self::assertEquals(
$this->getContent('post'),
$this->serialize(new Post('Post Title'))
);
self::assertEquals(
$this->getContent('image_post'),
$this->serialize(new ImagePost('Image Post Title'))
);

if ($this->hasDeserializer()) {
$this->assertEquals(
Expand Down Expand Up @@ -1265,6 +1275,33 @@ public function testPolymorphicObjects()
),
'Class is resolved correctly when concrete sub-class is used and no type is defined.'
);

self::assertEquals(
new Post('Post Title'),
$this->deserialize(
$this->getContent('post'),
'JMS\Serializer\Tests\Fixtures\Discriminator\Post'
),
'Class is resolved correctly when parent class is used and type is set.'
);

self::assertEquals(
new ImagePost('Image Post Title'),
$this->deserialize(
$this->getContent('image_post'),
'JMS\Serializer\Tests\Fixtures\Discriminator\Post'
),
'Class is resolved correctly when least supertype is used.'
);

self::assertEquals(
new ImagePost('Image Post Title'),
$this->deserialize(
$this->getContent('image_post'),
'JMS\Serializer\Tests\Fixtures\Discriminator\ImagePost'
),
'Class is resolved correctly when concrete sub-class is used and no type is defined.'
);
}
}

Expand Down
3 changes: 3 additions & 0 deletions tests/Serializer/JsonSerializationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ protected function getContent($key)
$outputs['date_interval'] = '"PT45M"';
$outputs['car'] = '{"km":5,"type":"car"}';
$outputs['car_without_type'] = '{"km":5}';
$outputs['post'] = '{"type":"post","title":"Post Title"}';
$outputs['image_post'] = '{"type":"image_post","title":"Image Post Title"}';
$outputs['image_post_without_type'] = '{"title":"Image Post Title"}';
$outputs['garage'] = '{"vehicles":[{"km":3,"type":"car"},{"km":1,"type":"moped"}]}';
$outputs['tree'] = '{"tree":{"children":[{"children":[{"children":[],"foo":"bar"}],"foo":"bar"}],"foo":"bar"}}';
$outputs['nullable_arrays'] = '{"empty_inline":[],"not_empty_inline":["not_empty_inline"],"empty_not_inline":[],"not_empty_not_inline":["not_empty_not_inline"],"empty_not_inline_skip":[],"not_empty_not_inline_skip":["not_empty_not_inline_skip"]}';
Expand Down
5 changes: 5 additions & 0 deletions tests/Serializer/xml/image_post.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<result>
<type><![CDATA[image_post]]></type>
<title><![CDATA[Image Post Title]]></title>
</result>
4 changes: 4 additions & 0 deletions tests/Serializer/xml/image_post_without_type.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<result>
<title><![CDATA[Image Post Title]]></title>
</result>
5 changes: 5 additions & 0 deletions tests/Serializer/xml/post.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<result>
<type><![CDATA[post]]></type>
<title><![CDATA[Post Title]]></title>
</result>
2 changes: 2 additions & 0 deletions tests/Serializer/yml/image_post.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
type: image_post
title: 'Image Post Title'
1 change: 1 addition & 0 deletions tests/Serializer/yml/image_post_without_type.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
title: 'Image Post Title'
2 changes: 2 additions & 0 deletions tests/Serializer/yml/post.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
type: post
title: 'Post Title'

0 comments on commit 3888192

Please sign in to comment.