Skip to content

Commit

Permalink
XML (de)serialization support
Browse files Browse the repository at this point in the history
  • Loading branch information
VasekPurchart committed Jan 27, 2017
1 parent 2856d39 commit c6373ec
Show file tree
Hide file tree
Showing 5 changed files with 288 additions and 14 deletions.
27 changes: 27 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,33 @@ var_dump($serializer->deserialize('{

If you are using this in an API, make sure you will catch this exception and send the consumer a response detailing this error, you can also write a custom message, the available values are listed in `InvalidEnumValueException::getAvailableValues()`.

### XML support

Unlike in JSON, in XML value types cannot be inferred directly from the values. So if you need to deserialize XML, you have to provide this type manually. You can do this by writing the type in the type definition - for the above example it would be `string`:

```php
<?php

namespace Consistence\JmsSerializer\Example\User;

use JMS\Serializer\Annotation as JMS;

class User extends \Consistence\ObjectPrototype
{

// ...

/**
* @JMS\Type("enum<Consistence\JmsSerializer\Example\User\Sex, string>")
* @var \Consistence\JmsSerializer\Example\User\Sex|null
*/
private $sex;

// ...

}
```

### Special support for mapped MultiEnums

Since the (de)serialization works only with the value the enum is representing, then in case of [MultiEnums](https://github.com/consistence/consistence/blob/master/docs/Enum/multi-enums.md) this would mean outputting the value of the internal bit mask. This could be useful if both the client and server use the same Enum objects, but otherwise this breaks the abstraction and is less readable for a human consumer as well.
Expand Down
113 changes: 103 additions & 10 deletions src/Enum/EnumSerializerHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Consistence\Enum\Enum;
use Consistence\Enum\MultiEnum;
use Consistence\Type\ArrayType\ArrayType;
use Consistence\Type\Type;

use JMS\Serializer\AbstractVisitor;
use JMS\Serializer\Context;
Expand Down Expand Up @@ -61,18 +62,20 @@ public static function getSubscribingMethods()
public function serializeEnum(VisitorInterface $visitor, Enum $enum, array $type, Context $context)
{
try {
return $this->serializeEnumValue($enum, $type);
return $this->serializeEnumValue($visitor, $enum, $type, $context);
} catch (\Consistence\JmsSerializer\Enum\MappedClassMismatchException $e) {
throw new \Consistence\JmsSerializer\Enum\SerializationInvalidValueException($this->getPropertyPath($context), $e);
}
}

/**
* @param \JMS\Serializer\VisitorInterface $visitor
* @param \Consistence\Enum\Enum $enum
* @param mixed[] $type
* @param \JMS\Serializer\Context $context
* @return mixed
*/
private function serializeEnumValue(Enum $enum, array $type)
private function serializeEnumValue(VisitorInterface $visitor, Enum $enum, array $type, Context $context)
{
if ($this->hasEnumClassParameter($type)) {
$mappedEnumClass = $this->getEnumClass($type);
Expand All @@ -82,13 +85,67 @@ private function serializeEnumValue(Enum $enum, array $type)
}
if ($this->hasAsSingleParameter($type)) {
$this->checkMultiEnum($actualEnumClass);
return array_values(ArrayType::mapValuesByCallback($enum->getEnums(), function (Enum $singleEnum) {
return $singleEnum->getValue();
}));
$arrayValueType = [
'name' => 'enum',
'params' => [
[
'name' => 'enum',
'params' => [
[
'name' => $mappedEnumClass::getSingleEnumClass(),
'params' => [],
],
],
],
],
];
return $visitor->visitArray(array_values($enum->getEnums()), $arrayValueType, $context);
}
}

return $enum->getValue();
return $this->serializationVisitType($visitor, $enum, $type, $context);
}

/**
* @param \JMS\Serializer\VisitorInterface $visitor
* @param \Consistence\Enum\Enum $enum
* @param mixed[] $typeMetadata
* @param \JMS\Serializer\Context $context
* @return mixed
*/
private function serializationVisitType(VisitorInterface $visitor, Enum $enum, array $typeMetadata, Context $context)
{
$value = $enum->getValue();
$valueType = EnumValueType::get(Type::getType($value));

return $this->visitType($visitor, $value, $valueType, $typeMetadata, $context);
}

/**
* @param \JMS\Serializer\VisitorInterface $visitor
* @param mixed $data
* @param \Consistence\JmsSerializer\Enum\EnumValueType $dataType
* @param mixed[] $typeMetadata
* @param \JMS\Serializer\Context $context
* @return mixed
*/
private function visitType(VisitorInterface $visitor, $data, EnumValueType $dataType, array $typeMetadata, Context $context)
{
switch (true) {
case $dataType->equalsValue(EnumValueType::INTEGER):
return $visitor->visitInteger($data, $typeMetadata, $context);
case $dataType->equalsValue(EnumValueType::STRING):
return $visitor->visitString($data, $typeMetadata, $context);
case $dataType->equalsValue(EnumValueType::FLOAT):
return $visitor->visitDouble($data, $typeMetadata, $context);
case $dataType->equalsValue(EnumValueType::BOOLEAN):
return $visitor->visitBoolean($data, $typeMetadata, $context);
// @codeCoverageIgnoreStart
// should never happen, other types are not allowed in Enums
default:
throw new \Exception('Unexpected type');
}
// @codeCoverageIgnoreEnd
}

/**
Expand All @@ -101,7 +158,7 @@ private function serializeEnumValue(Enum $enum, array $type)
public function deserializeEnum(VisitorInterface $visitor, $data, array $type, Context $context)
{
try {
return $this->deserializeEnumValue($data, $type);
return $this->deserializeEnumValue($visitor, $data, $type, $context);
} catch (\Consistence\Enum\InvalidEnumValueException $e) {
throw new \Consistence\JmsSerializer\Enum\DeserializationInvalidValueException($this->getFieldPath($visitor, $context), $e);
} catch (\Consistence\JmsSerializer\Enum\NotIterableValueException $e) {
Expand All @@ -110,11 +167,13 @@ public function deserializeEnum(VisitorInterface $visitor, $data, array $type, C
}

/**
* @param \JMS\Serializer\VisitorInterface $visitor
* @param mixed $data
* @param mixed[] $type
* @param \JMS\Serializer\Context $context
* @return \Consistence\Enum\Enum
*/
private function deserializeEnumValue($data, array $type)
private function deserializeEnumValue(VisitorInterface $visitor, $data, array $type, Context $context)
{
$enumClass = $this->getEnumClass($type);
if ($this->hasAsSingleParameter($type)) {
Expand All @@ -128,13 +187,30 @@ private function deserializeEnumValue($data, array $type)
throw new \Consistence\JmsSerializer\Enum\NotIterableValueException($data);
}
foreach ($data as $item) {
$singleEnums[] = $singleEnumClass::get($item);
$singleEnums[] = $singleEnumClass::get($this->deserializationVisitType($visitor, $item, $type, $context));
}

return $enumClass::getMultiByEnums($singleEnums);
}

return $enumClass::get($data);
return $enumClass::get($this->deserializationVisitType($visitor, $data, $type, $context));
}

/**
* @param \JMS\Serializer\VisitorInterface $visitor
* @param mixed $data
* @param mixed[] $typeMetadata
* @param \JMS\Serializer\Context $context
* @return mixed
*/
private function deserializationVisitType(VisitorInterface $visitor, $data, array $typeMetadata, Context $context)
{
$deserializationType = $this->findDeserializationType($typeMetadata);
if ($deserializationType === null) {
return $data;
}

return $this->visitType($visitor, $data, $deserializationType, $typeMetadata, $context);
}

/**
Expand Down Expand Up @@ -175,6 +251,23 @@ private function hasAsSingleParameter(array $type)
}) !== null;
}

/**
* @param mixed[] $type
* @return \Consistence\JmsSerializer\Enum\EnumValueType|null
*/
private function findDeserializationType(array $type)
{
$parameter = $this->findParameter($type, function (array $parameter) {
return EnumValueType::isValidValue($parameter['name']);
});

if ($parameter === null) {
return null;
}

return EnumValueType::get($parameter['name']);
}

/**
* @param mixed[] $type
* @param \Closure $callback
Expand Down
13 changes: 13 additions & 0 deletions src/Enum/EnumValueType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace Consistence\JmsSerializer\Enum;

class EnumValueType extends \Consistence\Enum\Enum
{

const INTEGER = 'integer';
const STRING = 'string';
const FLOAT = 'float';
const BOOLEAN = 'boolean';

}
Loading

0 comments on commit c6373ec

Please sign in to comment.