diff --git a/Annotation/Virtual.php b/Annotation/Virtual.php new file mode 100644 index 00000000..2b89e4c8 --- /dev/null +++ b/Annotation/Virtual.php @@ -0,0 +1,18 @@ + + */ +final class Virtual +{ + /** + * @Required + * @var string + */ + public $field; +} \ No newline at end of file diff --git a/Metadata/ClassMetadata.php b/Metadata/ClassMetadata.php index 0a5fb08e..70f8844a 100644 --- a/Metadata/ClassMetadata.php +++ b/Metadata/ClassMetadata.php @@ -38,6 +38,7 @@ class ClassMetadata extends MergeableClassMetadata public $preSerializeMethods = array(); public $postSerializeMethods = array(); public $postDeserializeMethods = array(); + public $virtualPropertyMethods = array(); public $xmlRootName; public $accessorOrder; public $customOrder; @@ -86,6 +87,11 @@ public function addPostDeserializeMethod(MethodMetadata $method) $this->postDeserializeMethods[] = $method; } + public function addVirtualPropertyMethod(MethodMetadata $method, $field) + { + $this->virtualPropertyMethods[$field] = $method; + } + public function merge(MergeableInterface $object) { if (!$object instanceof ClassMetadata) { @@ -114,6 +120,7 @@ public function serialize() $this->preSerializeMethods, $this->postSerializeMethods, $this->postDeserializeMethods, + $this->virtualPropertyMethods, $this->xmlRootName, $this->accessorOrder, $this->customOrder, @@ -127,6 +134,7 @@ public function unserialize($str) $this->preSerializeMethods, $this->postSerializeMethods, $this->postDeserializeMethods, + $this->virtualPropertyMethods, $this->xmlRootName, $this->accessorOrder, $this->customOrder, @@ -138,32 +146,32 @@ public function unserialize($str) private function sortProperties() { - switch ($this->accessorOrder) { - case self::ACCESSOR_ORDER_ALPHABETICAL: - ksort($this->propertyMetadata); - break; - - case self::ACCESSOR_ORDER_CUSTOM: + switch ($this->accessorOrder) { + case self::ACCESSOR_ORDER_ALPHABETICAL: + ksort($this->propertyMetadata); + break; + + case self::ACCESSOR_ORDER_CUSTOM: $order = $this->customOrder; uksort($this->propertyMetadata, function($a, $b) use ($order) { - $existsA = isset($order[$a]); - $existsB = isset($order[$b]); - - if (!$existsA && !$existsB) { - return 0; - } - - if (!$existsA) { - return 1; - } - - if (!$existsB) { - return -1; + $existsA = isset($order[$a]); + $existsB = isset($order[$b]); + + if (!$existsA && !$existsB) { + return 0; } - return $order[$a] < $order[$b] ? -1 : 1; + if (!$existsA) { + return 1; + } + + if (!$existsB) { + return -1; + } + + return $order[$a] < $order[$b] ? -1 : 1; }); - break; - } + break; + } } } \ No newline at end of file diff --git a/Metadata/Driver/AnnotationDriver.php b/Metadata/Driver/AnnotationDriver.php index 50bf5eb4..acd866ec 100644 --- a/Metadata/Driver/AnnotationDriver.php +++ b/Metadata/Driver/AnnotationDriver.php @@ -32,6 +32,7 @@ use JMS\SerializerBundle\Annotation\PostSerialize; use JMS\SerializerBundle\Annotation\PostDeserialize; use JMS\SerializerBundle\Annotation\PreSerialize; +use JMS\SerializerBundle\Annotation\Virtual; use Metadata\MethodMetadata; use Doctrine\Common\Annotations\Reader; use JMS\SerializerBundle\Annotation\Type; @@ -152,6 +153,9 @@ public function loadMetadataForClass(\ReflectionClass $class) } else if ($annot instanceof PostSerialize) { $classMetadata->addPostSerializeMethod(new MethodMetadata($name, $method->getName())); continue 2; + } else if ($annot instanceof Virtual) { + $classMetadata->addVirtualPropertyMethod( new MethodMetadata($name, $method->getName()), $annot->field); + continue 2; } } } diff --git a/Serializer/GenericSerializationVisitor.php b/Serializer/GenericSerializationVisitor.php index f566f337..6ed145bb 100644 --- a/Serializer/GenericSerializationVisitor.php +++ b/Serializer/GenericSerializationVisitor.php @@ -113,6 +113,10 @@ public function startVisitingObject(ClassMetadata $metadata, $data, $type) $this->dataStack->push($this->data); $this->data = array(); + + foreach( $metadata->virtualPropertyMethods as $field => $method ) { + $this->data[$field] = $method->invoke($data); + } } public function endVisitingObject(ClassMetadata $metadata, $data, $type) diff --git a/Serializer/XmlSerializationVisitor.php b/Serializer/XmlSerializationVisitor.php index 078e7fd4..2713ac56 100644 --- a/Serializer/XmlSerializationVisitor.php +++ b/Serializer/XmlSerializationVisitor.php @@ -143,6 +143,12 @@ public function startVisitingObject(ClassMetadata $metadata, $data, $type) } $this->hasValue = false; + + foreach( $metadata->virtualPropertyMethods as $field => $method ) { + $element = $this->getDocument()->createElement($field); + $element->appendChild($this->getDocument()->createCDATASection( (string) $method->invoke($data))); + $this->getCurrentNode()->appendChild($element); + } } public function visitProperty(PropertyMetadata $metadata, $object) diff --git a/Serializer/YamlSerializationVisitor.php b/Serializer/YamlSerializationVisitor.php index d6034b8e..d6434381 100644 --- a/Serializer/YamlSerializationVisitor.php +++ b/Serializer/YamlSerializationVisitor.php @@ -143,6 +143,10 @@ public function visitTraversable($data, $type) public function startVisitingObject(ClassMetadata $metadata, $data, $type) { + foreach( $metadata->virtualPropertyMethods as $field => $method ) { + $this->writer + ->writeln(Inline::dump($field).': ' . $method->invoke($data)); + } } public function visitProperty(PropertyMetadata $metadata, $data) diff --git a/Tests/Fixtures/ObjectWithVirtualProperty.php b/Tests/Fixtures/ObjectWithVirtualProperty.php new file mode 100644 index 00000000..2439a0a3 --- /dev/null +++ b/Tests/Fixtures/ObjectWithVirtualProperty.php @@ -0,0 +1,36 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SerializerBundle\Tests\Fixtures; + +use JMS\SerializerBundle\Annotation\SerializedName; +use JMS\SerializerBundle\Annotation\Virtual; + +class ObjectWithVirtualProperty +{ + + protected $existField = 'value'; + + /** + * + * @Virtual(field="foo") + */ + public function getValue() { + return 'bar'; + } +} \ No newline at end of file diff --git a/Tests/Serializer/BaseSerializationTest.php b/Tests/Serializer/BaseSerializationTest.php index 3fc212d7..23e72440 100644 --- a/Tests/Serializer/BaseSerializationTest.php +++ b/Tests/Serializer/BaseSerializationTest.php @@ -58,6 +58,7 @@ use JMS\SerializerBundle\Tests\Fixtures\CircularReferenceParent; use JMS\SerializerBundle\Tests\Fixtures\InlineParent; use JMS\SerializerBundle\Tests\Fixtures\GroupsObject; +use JMS\SerializerBundle\Tests\Fixtures\ObjectWithVirtualProperty; use JMS\SerializerBundle\Serializer\XmlSerializationVisitor; use Doctrine\Common\Annotations\AnnotationReader; use JMS\SerializerBundle\Metadata\Driver\AnnotationDriver; @@ -438,6 +439,10 @@ public function testGroups() $serializer->setGroups(array()); $this->assertEquals($this->getContent('groups_all'), $serializer->serialize($groupsObject, $this->getFormat())); } + + public function testVirtualProperty() { + $this->assertEquals($this->getContent('virtual_property'), $this->serialize(new ObjectWithVirtualProperty())); + } abstract protected function getContent($key); abstract protected function getFormat(); diff --git a/Tests/Serializer/JsonSerializationTest.php b/Tests/Serializer/JsonSerializationTest.php index f6e741b7..a5772b8e 100644 --- a/Tests/Serializer/JsonSerializationTest.php +++ b/Tests/Serializer/JsonSerializationTest.php @@ -64,6 +64,7 @@ protected function getContent($key) $outputs['groups_all'] = '{"foo":"foo","foobar":"foobar","bar":"bar","none":"none"}'; $outputs['groups_foo'] = '{"foo":"foo","foobar":"foobar"}'; $outputs['groups_foobar'] = '{"foo":"foo","foobar":"foobar","bar":"bar"}'; + $outputs['virtual_property'] = '{"foo":"bar","exist_field":"value"}'; } if (!isset($outputs[$key])) { diff --git a/Tests/Serializer/xml/virtual_property.xml b/Tests/Serializer/xml/virtual_property.xml new file mode 100644 index 00000000..549b92a4 --- /dev/null +++ b/Tests/Serializer/xml/virtual_property.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/Tests/Serializer/yml/virtual_property.yml b/Tests/Serializer/yml/virtual_property.yml new file mode 100644 index 00000000..b2376514 --- /dev/null +++ b/Tests/Serializer/yml/virtual_property.yml @@ -0,0 +1,2 @@ +foo: bar +exist_field: value