From 3f46809b02363316532ad035331d0a14a4852b92 Mon Sep 17 00:00:00 2001 From: Mauro Foti Date: Tue, 18 Feb 2014 17:20:13 +0100 Subject: [PATCH] XmlList and XmlMap Namespace support Adding support for namespace param for XmlList and XmlMap Metadata @XmlList(entry="event", inline = true, namespace="http://mynamespace.net" ) --- doc/reference/annotations.rst | 2 + doc/reference/xml_reference.rst | 2 +- doc/reference/yml_reference.rst | 2 + .../Serializer/Annotation/XmlCollection.php | 5 + .../Metadata/Driver/AnnotationDriver.php | 2 + .../Serializer/Metadata/Driver/XmlDriver.php | 8 ++ .../Serializer/Metadata/Driver/YamlDriver.php | 9 ++ .../Serializer/Metadata/PropertyMetadata.php | 1 + .../Serializer/XmlDeserializationVisitor.php | 83 +++++++------- .../Serializer/XmlSerializationVisitor.php | 89 ++++++++++----- .../Serializer/Tests/Fixtures/BlogPost.php | 11 ++ .../Fixtures/ObjectWithNamespacesAndList.php | 103 ++++++++++++++++++ tests/JMS/Serializer/Tests/Fixtures/Tag.php | 42 +++++++ .../Serializer/BaseSerializationTest.php | 5 + .../Serializer/JsonSerializationTest.php | 4 +- .../Tests/Serializer/XmlSerializationTest.php | 28 +++++ .../Tests/Serializer/xml/blog_post.xml | 6 + .../xml/object_with_namespaces_and_list.xml | 29 +++++ .../Tests/Serializer/yml/blog_post.yml | 5 + .../Serializer/yml/blog_post_unauthored.yml | 1 + 20 files changed, 370 insertions(+), 67 deletions(-) create mode 100644 tests/JMS/Serializer/Tests/Fixtures/ObjectWithNamespacesAndList.php create mode 100644 tests/JMS/Serializer/Tests/Fixtures/Tag.php create mode 100644 tests/JMS/Serializer/Tests/Serializer/xml/object_with_namespaces_and_list.xml diff --git a/doc/reference/annotations.rst b/doc/reference/annotations.rst index 25e5f3695..62b726f4e 100644 --- a/doc/reference/annotations.rst +++ b/doc/reference/annotations.rst @@ -475,6 +475,8 @@ Resulting XML: +You can also specify the entry tag namespace using the ``namespace`` attribute (``@XmlList(inline = true, entry = "comment", namespace="http://www.example.com/ns")``). + @XmlMap ~~~~~~~ Similar to @XmlList, but the keys of the array are meaningful. diff --git a/doc/reference/xml_reference.rst b/doc/reference/xml_reference.rst index 214113d8a..68bf9dbe9 100644 --- a/doc/reference/xml_reference.rst +++ b/doc/reference/xml_reference.rst @@ -32,7 +32,7 @@ XML Reference your type contains "<" or ">" characters. --> - + diff --git a/doc/reference/yml_reference.rst b/doc/reference/yml_reference.rst index 05db24bf7..a7728596c 100644 --- a/doc/reference/yml_reference.rst +++ b/doc/reference/yml_reference.rst @@ -44,10 +44,12 @@ YAML Reference xml_list: inline: true entry_name: foo + namespace: http://www.w3.org/2005/Atom xml_map: inline: true key_attribute_name: foo entry_name: bar + namespace: http://www.w3.org/2005/Atom xml_attribute_map: true xml_element: cdata: false diff --git a/src/JMS/Serializer/Annotation/XmlCollection.php b/src/JMS/Serializer/Annotation/XmlCollection.php index dd98dc53b..243176aee 100644 --- a/src/JMS/Serializer/Annotation/XmlCollection.php +++ b/src/JMS/Serializer/Annotation/XmlCollection.php @@ -29,4 +29,9 @@ abstract class XmlCollection * @var boolean */ public $inline = false; + + /** + * @var string + */ + public $namespace; } diff --git a/src/JMS/Serializer/Metadata/Driver/AnnotationDriver.php b/src/JMS/Serializer/Metadata/Driver/AnnotationDriver.php index 30fe6142d..56b1b3a72 100644 --- a/src/JMS/Serializer/Metadata/Driver/AnnotationDriver.php +++ b/src/JMS/Serializer/Metadata/Driver/AnnotationDriver.php @@ -170,10 +170,12 @@ public function loadMetadataForClass(\ReflectionClass $class) $propertyMetadata->xmlCollection = true; $propertyMetadata->xmlCollectionInline = $annot->inline; $propertyMetadata->xmlEntryName = $annot->entry; + $propertyMetadata->xmlEntryNamespace = $annot->namespace; } elseif ($annot instanceof XmlMap) { $propertyMetadata->xmlCollection = true; $propertyMetadata->xmlCollectionInline = $annot->inline; $propertyMetadata->xmlEntryName = $annot->entry; + $propertyMetadata->xmlEntryNamespace = $annot->namespace; $propertyMetadata->xmlKeyAttribute = $annot->keyAttribute; } elseif ($annot instanceof XmlKeyValuePairs) { $propertyMetadata->xmlKeyValuePairs = true; diff --git a/src/JMS/Serializer/Metadata/Driver/XmlDriver.php b/src/JMS/Serializer/Metadata/Driver/XmlDriver.php index 5383abac4..8c6c35093 100644 --- a/src/JMS/Serializer/Metadata/Driver/XmlDriver.php +++ b/src/JMS/Serializer/Metadata/Driver/XmlDriver.php @@ -172,6 +172,10 @@ protected function loadMetadataFromFile(\ReflectionClass $class, $path) if (isset($colConfig->attributes()->{'entry-name'})) { $pMetadata->xmlEntryName = (string) $colConfig->attributes()->{'entry-name'}; } + + if (isset($colConfig->attributes()->namespace)) { + $pMetadata->xmlEntryNamespace = (string) $colConfig->attributes()->namespace; + } } if (isset($pElem->{'xml-map'})) { @@ -186,6 +190,10 @@ protected function loadMetadataFromFile(\ReflectionClass $class, $path) $pMetadata->xmlEntryName = (string) $colConfig->attributes()->{'entry-name'}; } + if (isset($colConfig->attributes()->namespace)) { + $pMetadata->xmlEntryNamespace = (string) $colConfig->attributes()->namespace; + } + if (isset($colConfig->attributes()->{'key-attribute-name'})) { $pMetadata->xmlKeyAttribute = (string) $colConfig->attributes()->{'key-attribute-name'}; } diff --git a/src/JMS/Serializer/Metadata/Driver/YamlDriver.php b/src/JMS/Serializer/Metadata/Driver/YamlDriver.php index bb1f56b2a..728e864ce 100644 --- a/src/JMS/Serializer/Metadata/Driver/YamlDriver.php +++ b/src/JMS/Serializer/Metadata/Driver/YamlDriver.php @@ -119,6 +119,10 @@ protected function loadMetadataFromFile(\ReflectionClass $class, $file) if (isset($colConfig['entry_name'])) { $pMetadata->xmlEntryName = (string) $colConfig['entry_name']; } + + if (isset($colConfig['namespace'])) { + $pMetadata->xmlEntryNamespace = (string) $colConfig['namespace']; + } } if (isset($pConfig['xml_map'])) { @@ -133,9 +137,14 @@ protected function loadMetadataFromFile(\ReflectionClass $class, $file) $pMetadata->xmlEntryName = (string) $colConfig['entry_name']; } + if (isset($colConfig['namespace'])) { + $pMetadata->xmlEntryNamespace = (string) $colConfig['namespace']; + } + if (isset($colConfig['key_attribute_name'])) { $pMetadata->xmlKeyAttribute = $colConfig['key_attribute_name']; } + } if (isset($pConfig['xml_element'])) { diff --git a/src/JMS/Serializer/Metadata/PropertyMetadata.php b/src/JMS/Serializer/Metadata/PropertyMetadata.php index 34fa8478e..169476f16 100644 --- a/src/JMS/Serializer/Metadata/PropertyMetadata.php +++ b/src/JMS/Serializer/Metadata/PropertyMetadata.php @@ -35,6 +35,7 @@ class PropertyMetadata extends BasePropertyMetadata public $xmlCollection = false; public $xmlCollectionInline = false; public $xmlEntryName; + public $xmlEntryNamespace; public $xmlKeyAttribute; public $xmlAttribute = false; public $xmlValue = false; diff --git a/src/JMS/Serializer/XmlDeserializationVisitor.php b/src/JMS/Serializer/XmlDeserializationVisitor.php index fe2fd7d6f..6d4c2e8bd 100644 --- a/src/JMS/Serializer/XmlDeserializationVisitor.php +++ b/src/JMS/Serializer/XmlDeserializationVisitor.php @@ -29,6 +29,7 @@ class XmlDeserializationVisitor extends AbstractVisitor { private $objectStack; private $metadataStack; + private $objectMetadataStack; private $currentObject; private $currentMetadata; private $result; @@ -46,6 +47,7 @@ public function setNavigator(GraphNavigator $navigator) $this->navigator = $navigator; $this->objectStack = new \SplStack; $this->metadataStack = new \SplStack; + $this->objectMetadataStack = new \SplStack; $this->result = null; } @@ -74,6 +76,7 @@ public function prepare($data) } $doc = simplexml_load_string($data); + libxml_use_internal_errors($previous); libxml_disable_entity_loader($previousEntityLoaderState); @@ -144,8 +147,14 @@ public function visitDouble($data, array $type, Context $context) public function visitArray($data, array $type, Context $context) { $entryName = null !== $this->currentMetadata && $this->currentMetadata->xmlEntryName ? $this->currentMetadata->xmlEntryName : 'entry'; + $namespace = null !== $this->currentMetadata && $this->currentMetadata->xmlEntryNamespace ? $this->currentMetadata->xmlEntryNamespace : null; + + if ($namespace === null && $this->objectMetadataStack->count()) { + $classMetadata = $this->objectMetadataStack->top(); + $namespace = isset($classMetadata->xmlNamespaces[''])?$classMetadata->xmlNamespaces['']:$namespace; + } - if ( ! isset($data->$entryName)) { + if ( ! isset($data->$entryName) ) { if (null === $this->result) { return $this->result = array(); } @@ -159,11 +168,13 @@ public function visitArray($data, array $type, Context $context) case 1: $result = array(); + if (null === $this->result) { $this->result = &$result; } - foreach ($data->$entryName as $v) { + $nodes = $data->children($namespace)->$entryName; + foreach ($nodes as $v) { $result[] = $this->navigator->accept($v, $type['params'][0], $context); } @@ -180,12 +191,14 @@ public function visitArray($data, array $type, Context $context) $this->result = &$result; } - foreach ($data->$entryName as $v) { - if ( ! isset($v[$this->currentMetadata->xmlKeyAttribute])) { + $nodes = $data->children($namespace)->$entryName; + foreach ($nodes as $v) { + $attrs = $v->attributes(); + if ( ! isset($attrs[$this->currentMetadata->xmlKeyAttribute])) { throw new RuntimeException(sprintf('The key attribute "%s" must be set for each entry of the map.', $this->currentMetadata->xmlKeyAttribute)); } - $k = $this->navigator->accept($v[$this->currentMetadata->xmlKeyAttribute], $keyType, $context); + $k = $this->navigator->accept($attrs[$this->currentMetadata->xmlKeyAttribute], $keyType, $context); $result[$k] = $this->navigator->accept($v, $entryType, $context); } @@ -199,7 +212,7 @@ public function visitArray($data, array $type, Context $context) public function startVisitingObject(ClassMetadata $metadata, $object, array $type, Context $context) { $this->setCurrentObject($object); - + $this->objectMetadataStack->push($metadata); if (null === $this->result) { $this->result = $this->currentObject; } @@ -213,22 +226,11 @@ public function visitProperty(PropertyMetadata $metadata, $data, Context $contex throw new RuntimeException(sprintf('You must define a type for %s::$%s.', $metadata->reflection->class, $metadata->name)); } - if ($metadata->xmlAttribute) { - if ('' !== $namespace = (string) $metadata->xmlNamespace) { - $registeredNamespaces = $data->getDocNamespaces(); - if (false === $prefix = array_search($namespace, $registeredNamespaces)) { - $prefix = uniqid('ns-'); - $data->registerXPathNamespace($prefix, $namespace); - } - $attributeName = ($prefix === '') ? $name : $prefix.':'.$name; - $nodes = $data->xpath('./@'.$attributeName); - if ( ! empty($nodes)) { - $v = (string) reset($nodes); - $metadata->reflection->setValue($this->currentObject, $v); - } + if ($metadata->xmlAttribute) { - } elseif (isset($data[$name])) { - $v = $this->navigator->accept($data[$name], $metadata->type, $context); + $attributes = $data->attributes($metadata->xmlNamespace); + if (isset($attributes[$name])) { + $v = $this->navigator->accept($attributes[$name], $metadata->type, $context); $metadata->reflection->setValue($this->currentObject, $v); } @@ -244,8 +246,8 @@ public function visitProperty(PropertyMetadata $metadata, $data, Context $contex if ($metadata->xmlCollection) { $enclosingElem = $data; - if ( ! $metadata->xmlCollectionInline && isset($data->$name)) { - $enclosingElem = $data->$name; + if (!$metadata->xmlCollectionInline) { + $enclosingElem = $data->children($metadata->xmlNamespace)->$name; } $this->setCurrentMetadata($metadata); @@ -256,23 +258,29 @@ public function visitProperty(PropertyMetadata $metadata, $data, Context $contex return; } - if ('' !== $namespace = (string) $metadata->xmlNamespace) { - $registeredNamespaces = $data->getDocNamespaces(); - if (false === $prefix = array_search($namespace, $registeredNamespaces)) { - $prefix = uniqid('ns-'); - $data->registerXPathNamespace($prefix, $namespace); - } - $elementName = ($prefix === '') ? $name : $prefix.':'.$name; - $nodes = $data->xpath('./'.$elementName); - if (empty($nodes)) { + if ($metadata->xmlNamespace) { + $node = $data->children($metadata->xmlNamespace)->$name; + if (!$node->count()) { return; } - $node = reset($nodes); } else { - if ( ! isset($data->$name)) { - return; + + $namespaces = $data->getDocNamespaces(); + + if (isset($namespaces[''])) { + $prefix = uniqid('ns-'); + $data->registerXPathNamespace($prefix, $namespaces['']); + $nodes = $data->xpath('./'.$prefix. ':'.$name ); + if (empty($nodes)) { + return; + } + $node = reset($nodes); + } else { + if (!isset($data->$name)) { + return; + } + $node = $data->$name; } - $node = $data->$name; } $v = $this->navigator->accept($node, $metadata->type, $context); @@ -289,6 +297,7 @@ public function visitProperty(PropertyMetadata $metadata, $data, Context $contex public function endVisitingObject(ClassMetadata $metadata, $data, array $type, Context $context) { $rs = $this->currentObject; + $this->objectMetadataStack->pop(); $this->revertCurrentObject(); return $rs; @@ -359,7 +368,7 @@ private function getDomDocumentTypeEntitySubset(\DOMDocumentType $child, $data) if (null !== $child->internalSubset) { return str_replace(array("\n", "\r"), '', $child->internalSubset); } - + $startPos = $endPos = stripos($data, 'objectMetadataStack = new \SplStack; + } public function setDefaultRootName($name, $namespace = null) { @@ -161,11 +168,12 @@ public function visitArray($data, array $type, Context $context) $entryName = (null !== $this->currentMetadata && null !== $this->currentMetadata->xmlEntryName) ? $this->currentMetadata->xmlEntryName : 'entry'; $keyAttributeName = (null !== $this->currentMetadata && null !== $this->currentMetadata->xmlKeyAttribute) ? $this->currentMetadata->xmlKeyAttribute : null; + $namespace = (null !== $this->currentMetadata && null !== $this->currentMetadata->xmlEntryNamespace) ? $this->currentMetadata->xmlEntryNamespace : null; foreach ($data as $k => $v) { $tagName = (null !== $this->currentMetadata && $this->currentMetadata->xmlKeyValuePairs && $this->isElementNameValid($k)) ? $k : $entryName; - $entryNode = $this->document->createElement($tagName); + $entryNode = $this->createElement($tagName, $namespace); $this->currentNode->appendChild($entryNode); $this->setCurrentNode($entryNode); @@ -183,23 +191,27 @@ public function visitArray($data, array $type, Context $context) public function startVisitingObject(ClassMetadata $metadata, $data, array $type, Context $context) { + $this->objectMetadataStack->push($metadata); + if (null === $this->document) { $this->document = $this->createDocument(null, null, false); if ($metadata->xmlRootName) { $rootName = $metadata->xmlRootName; - $rootNamespace = $metadata->xmlRootNamespace; + $rootNamespace = $metadata->xmlRootNamespace?:$this->getClassDefaultNamespace($metadata); } else { $rootName = $this->defaultRootName; $rootNamespace = $this->defaultRootNamespace; } + if ($rootNamespace) { $this->currentNode = $this->document->createElementNS($rootNamespace, $rootName); } else { $this->currentNode = $this->document->createElement($rootName); } + $this->document->appendChild($this->currentNode); } - + $this->addNamespaceAttributes($metadata, $this->currentNode); $this->hasValue = false; @@ -219,17 +231,10 @@ public function visitProperty(PropertyMetadata $metadata, $object, Context $cont $this->revertCurrentMetadata(); if ( ! $node instanceof \DOMCharacterData) { - throw new RuntimeException(sprintf('Unsupported value for XML attribute. Expected character data, but got %s.', json_encode($v))); + throw new RuntimeException(sprintf('Unsupported value for XML attribute for %s. Expected character data, but got %s.', $metadata->name, json_encode($v))); } $attributeName = $this->namingStrategy->translateName($metadata); - if ('' !== $namespace = (string) $metadata->xmlNamespace) { - if ( ! $prefix = $this->currentNode->lookupPrefix($namespace)) { - $prefix = 'ns-'.substr(sha1($namespace), 0, 8); - } - $this->currentNode->setAttributeNS($namespace, $prefix.':'.$attributeName, $node->nodeValue); - } else { - $this->currentNode->setAttribute($attributeName, $node->nodeValue); - } + $this->setAttributeOnNode($this->currentNode, $attributeName, $node->nodeValue, $metadata->xmlNamespace); return; } @@ -269,14 +274,7 @@ public function visitProperty(PropertyMetadata $metadata, $object, Context $cont throw new RuntimeException(sprintf('Unsupported value for a XML attribute map value. Expected character data, but got %s.', json_encode($v))); } - if ('' !== $namespace = (string) $metadata->xmlNamespace) { - if ( ! $prefix = $this->currentNode->lookupPrefix($namespace)) { - $prefix = 'ns-'.substr(sha1($namespace), 0, 8); - } - $this->currentNode->setAttributeNS($namespace, $prefix.':'.$key, $node->nodeValue); - } else { - $this->currentNode->setAttribute($key, $node->nodeValue); - } + $this->setAttributeOnNode($this->currentNode, $key, $node->nodeValue, $metadata->xmlNamespace); } return; @@ -284,13 +282,12 @@ public function visitProperty(PropertyMetadata $metadata, $object, Context $cont if ($addEnclosingElement = ( ! $metadata->xmlCollection || ! $metadata->xmlCollectionInline) && ! $metadata->inline) { $elementName = $this->namingStrategy->translateName($metadata); - if ('' !== $namespace = (string) $metadata->xmlNamespace) { - if ( ! $prefix = $this->currentNode->lookupPrefix($namespace)) { - $prefix = 'ns-'.substr(sha1($namespace), 0, 8); - } - $element = $this->document->createElementNS($namespace, $prefix.':'.$elementName); + + if (null !== $metadata->xmlNamespace) { + $element = $this->createElement($elementName, $metadata->xmlNamespace); } else { - $element = $this->document->createElement($elementName); + $defaultNamespace = $this->getClassDefaultNamespace($this->objectMetadataStack->top()); + $element = $this->createElement($elementName, $defaultNamespace); } $this->setCurrentNode($element); } @@ -317,6 +314,7 @@ public function visitProperty(PropertyMetadata $metadata, $object, Context $cont public function endVisitingObject(ClassMetadata $metadata, $data, array $type, Context $context) { + $this->objectMetadataStack->pop(); } public function getResult() @@ -421,7 +419,7 @@ private function attachNullNamespace() $this->nullWasVisited = true; } } - + /** * Adds namespace attributes to the XML root element * @@ -438,4 +436,41 @@ private function addNamespaceAttributes(ClassMetadata $metadata, \DOMElement $el $element->setAttributeNS('http://www.w3.org/2000/xmlns/', $attribute, $uri); } } + + private function createElement($tagName, $namespace = null) + { + if (null !== $namespace) { + + if ($this->currentNode->isDefaultNamespace($namespace)) { + return $this->document->createElementNS($namespace, $tagName); + } else { + if (!$prefix = $this->currentNode->lookupPrefix($namespace)) { + $prefix = 'ns-'. substr(sha1($namespace), 0, 8); + } + return $this->document->createElementNS($namespace, $prefix . ':' . $tagName); + } + + + } else { + return $this->document->createElement($tagName); + } + } + + private function setAttributeOnNode(\DOMElement $node, $name, $value, $namespace = null) + { + if (null !== $namespace) { + if (!$prefix = $node->lookupPrefix($namespace)) { + $prefix = 'ns-'. substr(sha1($namespace), 0, 8); + } + $node->setAttributeNS($namespace, $prefix.':'.$name, $value); + } else { + $node->setAttribute($name, $value); + } + } + + private function getClassDefaultNamespace(ClassMetadata $metadata) + { + return (isset($metadata->xmlNamespaces[''])?$metadata->xmlNamespaces['']:null); + } + } diff --git a/tests/JMS/Serializer/Tests/Fixtures/BlogPost.php b/tests/JMS/Serializer/Tests/Fixtures/BlogPost.php index 48d9295d7..a0f527901 100644 --- a/tests/JMS/Serializer/Tests/Fixtures/BlogPost.php +++ b/tests/JMS/Serializer/Tests/Fixtures/BlogPost.php @@ -107,6 +107,12 @@ class BlogPost */ private $publisher; + /** + * @Type("array") + * @XmlList(inline=true, entry="tag", namespace="http://purl.org/dc/elements/1.1/"); + */ + private $tag; + public function __construct($title, Author $author, \DateTime $createdAt, Publisher $publisher) { $this->title = $title; @@ -136,4 +142,9 @@ public function addComment(Comment $comment) $this->comments->add($comment); $this->comments2->add($comment); } + + public function addTag(Tag $tag) + { + $this->tag[] = $tag; + } } diff --git a/tests/JMS/Serializer/Tests/Fixtures/ObjectWithNamespacesAndList.php b/tests/JMS/Serializer/Tests/Fixtures/ObjectWithNamespacesAndList.php new file mode 100644 index 000000000..edacae734 --- /dev/null +++ b/tests/JMS/Serializer/Tests/Fixtures/ObjectWithNamespacesAndList.php @@ -0,0 +1,103 @@ + + * + * 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\Serializer\Tests\Fixtures; + +use JMS\Serializer\Annotation\SerializedName; +use JMS\Serializer\Annotation\Type; +use JMS\Serializer\Annotation\XmlRoot; +use JMS\Serializer\Annotation\XmlNamespace; +use JMS\Serializer\Annotation\XmlElement; +use JMS\Serializer\Annotation\XmlList; +use JMS\Serializer\Annotation\XmlMap; +/** + * @XmlRoot("ObjectWithNamespacesAndList", namespace="http://example.com/namespace") + * @XmlNamespace(uri="http://example.com/namespace") + * @XmlNamespace(uri="http://example.com/namespace2", prefix="x") + */ +class ObjectWithNamespacesAndList +{ + /** + * @Type("string") + * @SerializedName("name") + * @XmlElement(namespace="http://example.com/namespace") + */ + public $name; + /** + * @Type("string") + * @SerializedName("name") + * @XmlElement(namespace="http://example.com/namespace2") + */ + public $nameAlternativeB; + + /** + * @Type("array") + * @SerializedName("phones") + * @XmlElement(namespace="http://example.com/namespace2") + * @XmlList(inline = false, entry = "phone", namespace="http://example.com/namespace2") + */ + public $phones; + /** + * @Type("array") + * @SerializedName("addresses") + * @XmlElement(namespace="http://example.com/namespace2") + * @XmlMap(inline = false, entry = "address", keyAttribute = "id", namespace="http://example.com/namespace2") + */ + public $addresses; + + /** + * @Type("array") + * @SerializedName("phones") + * @XmlList(inline = true, entry = "phone", namespace="http://example.com/namespace") + */ + public $phonesAlternativeB; + /** + * @Type("array") + * @SerializedName("addresses") + * @XmlMap(inline = true, entry = "address", keyAttribute = "id", namespace="http://example.com/namespace") + */ + public $addressesAlternativeB; + + /** + * @Type("array") + * @SerializedName("phones") + * @XmlList(inline = true, entry = "phone", namespace="http://example.com/namespace2") + */ + public $phonesAlternativeC; + /** + * @Type("array") + * @SerializedName("addresses") + * @XmlMap(inline = true, entry = "address", keyAttribute = "id", namespace="http://example.com/namespace2") + */ + public $addressesAlternativeC; + + /** + * @Type("array") + * @SerializedName("phones") + * @XmlList(inline = false, entry = "phone") + */ + public $phonesAlternativeD; + /** + * @Type("array") + * @SerializedName("addresses") + * @XmlMap(inline = false, entry = "address", keyAttribute = "id") + */ + public $addressesAlternativeD; + +} + diff --git a/tests/JMS/Serializer/Tests/Fixtures/Tag.php b/tests/JMS/Serializer/Tests/Fixtures/Tag.php new file mode 100644 index 000000000..36ba0a4ef --- /dev/null +++ b/tests/JMS/Serializer/Tests/Fixtures/Tag.php @@ -0,0 +1,42 @@ + + * + * 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\Serializer\Tests\Fixtures; + +use JMS\Serializer\Annotation as JMS; + + +/** + * @JMS\XmlRoot("tag") + * @JMS\XmlNamespace(uri="http://purl.org/dc/elements/1.1/", prefix="dc") + */ +class Tag { + + /** + * @JMS\XmlElement(cdata=false) + * @JMS\Type("string") + */ + public $name; + + function __construct($name) + { + $this->name = $name; + } + + +} \ No newline at end of file diff --git a/tests/JMS/Serializer/Tests/Serializer/BaseSerializationTest.php b/tests/JMS/Serializer/Tests/Serializer/BaseSerializationTest.php index 470ecae63..cfee94d50 100644 --- a/tests/JMS/Serializer/Tests/Serializer/BaseSerializationTest.php +++ b/tests/JMS/Serializer/Tests/Serializer/BaseSerializationTest.php @@ -29,6 +29,7 @@ use JMS\Serializer\Tests\Fixtures\Garage; use JMS\Serializer\Tests\Fixtures\InlineChildEmpty; use JMS\Serializer\Tests\Fixtures\NamedDateTimeArraysObject; +use JMS\Serializer\Tests\Fixtures\Tag; use JMS\Serializer\Tests\Fixtures\Tree; use JMS\Serializer\Tests\Fixtures\VehicleInterfaceGarage; use PhpCollection\Sequence; @@ -377,6 +378,9 @@ public function testBlogPost() $post = new BlogPost('This is a nice title.', $author = new Author('Foo Bar'), new \DateTime('2011-07-30 00:00', new \DateTimeZone('UTC')), new Publisher('Bar Foo')); $post->addComment($comment = new Comment($author, 'foo')); + $post->addTag($tag1 = New Tag("tag1")); + $post->addTag($tag2 = New Tag("tag2")); + $this->assertEquals($this->getContent('blog_post'), $this->serialize($post)); if ($this->hasDeserializer()) { @@ -388,6 +392,7 @@ public function testBlogPost() $this->assertAttributeEquals(new ArrayCollection(array($comment)), 'comments', $deserialized); $this->assertAttributeEquals(new Sequence(array($comment)), 'comments2', $deserialized); $this->assertAttributeEquals($author, 'author', $deserialized); + $this->assertAttributeEquals(array($tag1, $tag2), 'tag', $deserialized); } } diff --git a/tests/JMS/Serializer/Tests/Serializer/JsonSerializationTest.php b/tests/JMS/Serializer/Tests/Serializer/JsonSerializationTest.php index dee76d326..0e434ef9d 100644 --- a/tests/JMS/Serializer/Tests/Serializer/JsonSerializationTest.php +++ b/tests/JMS/Serializer/Tests/Serializer/JsonSerializationTest.php @@ -52,8 +52,8 @@ protected function getContent($key) $outputs['array_mixed'] = '["foo",1,true,{"foo":"foo","moo":"bar","camel_case":"boo"},[1,3,true]]'; $outputs['array_datetimes_object'] = '{"array_with_default_date_time":["2047-01-01T12:47:47+0000","2013-12-05T00:00:00+0000"],"array_with_formatted_date_time":["01.01.2047 12:47:47","05.12.2013 00:00:00"]}'; $outputs['array_named_datetimes_object'] = '{"named_array_with_formatted_date":{"testdate1":"01.01.2047 12:47:47","testdate2":"05.12.2013 00:00:00"}}'; - $outputs['blog_post'] = '{"id":"what_a_nice_id","title":"This is a nice title.","created_at":"2011-07-30T00:00:00+0000","is_published":false,"etag":"1edf9bf60a32d89afbb85b2be849e3ceed5f5b10","comments":[{"author":{"full_name":"Foo Bar"},"text":"foo"}],"comments2":[{"author":{"full_name":"Foo Bar"},"text":"foo"}],"metadata":{"foo":"bar"},"author":{"full_name":"Foo Bar"},"publisher":{"pub_name":"Bar Foo"}}'; - $outputs['blog_post_unauthored'] = '{"id":"what_a_nice_id","title":"This is a nice title.","created_at":"2011-07-30T00:00:00+0000","is_published":false,"etag":"1edf9bf60a32d89afbb85b2be849e3ceed5f5b10","comments":[],"comments2":[],"metadata":{"foo":"bar"},"author":null,"publisher":null}'; + $outputs['blog_post'] = '{"id":"what_a_nice_id","title":"This is a nice title.","created_at":"2011-07-30T00:00:00+0000","is_published":false,"etag":"1edf9bf60a32d89afbb85b2be849e3ceed5f5b10","comments":[{"author":{"full_name":"Foo Bar"},"text":"foo"}],"comments2":[{"author":{"full_name":"Foo Bar"},"text":"foo"}],"metadata":{"foo":"bar"},"author":{"full_name":"Foo Bar"},"publisher":{"pub_name":"Bar Foo"},"tag":[{"name":"tag1"},{"name":"tag2"}]}'; + $outputs['blog_post_unauthored'] = '{"id":"what_a_nice_id","title":"This is a nice title.","created_at":"2011-07-30T00:00:00+0000","is_published":false,"etag":"1edf9bf60a32d89afbb85b2be849e3ceed5f5b10","comments":[],"comments2":[],"metadata":{"foo":"bar"},"author":null,"publisher":null,"tag":null}'; $outputs['price'] = '{"price":3}'; $outputs['currency_aware_price'] = '{"currency":"EUR","amount":2.34}'; $outputs['order'] = '{"cost":{"price":12.34}}'; diff --git a/tests/JMS/Serializer/Tests/Serializer/XmlSerializationTest.php b/tests/JMS/Serializer/Tests/Serializer/XmlSerializationTest.php index 0a66739f7..01f8fe371 100644 --- a/tests/JMS/Serializer/Tests/Serializer/XmlSerializationTest.php +++ b/tests/JMS/Serializer/Tests/Serializer/XmlSerializationTest.php @@ -35,6 +35,7 @@ use JMS\Serializer\Tests\Fixtures\Input; use JMS\Serializer\Tests\Fixtures\SimpleClassObject; use JMS\Serializer\Tests\Fixtures\SimpleSubClassObject; +use JMS\Serializer\Tests\Fixtures\ObjectWithNamespacesAndList; class XmlSerializationTest extends BaseSerializationTest { @@ -159,6 +160,33 @@ public function testVirtualXmlMap() $this->serialize(new ObjectWithVirtualXmlProperties(), SerializationContext::create()->setGroups(array('map'))) ); } + public function testObjectWithNamespacesAndList() + { + $object = new ObjectWithNamespacesAndList(); + $object->name = 'name'; + $object->nameAlternativeB = 'nameB'; + + $object->phones = array('111', '222'); + $object->addresses = array('A'=>'Street 1', 'B'=>'Street 2'); + + $object->phonesAlternativeB = array('555', '666'); + $object->addressesAlternativeB = array('A'=>'Street 5', 'B'=>'Street 6'); + + $object->phonesAlternativeC = array('777', '888'); + $object->addressesAlternativeC = array('A'=>'Street 7', 'B'=>'Street 8'); + + $object->phonesAlternativeD = array('999', 'AAA'); + $object->addressesAlternativeD = array('A'=>'Street 9', 'B'=>'Street A'); + + $this->assertEquals( + $this->getContent('object_with_namespaces_and_list'), + $this->serialize($object, SerializationContext::create()) + ); + $this->assertEquals( + $object, + $this->deserialize($this->getContent('object_with_namespaces_and_list'), get_class($object)) + ); + } public function testArrayKeyValues() { diff --git a/tests/JMS/Serializer/Tests/Serializer/xml/blog_post.xml b/tests/JMS/Serializer/Tests/Serializer/xml/blog_post.xml index bea1e161f..0efe2865c 100644 --- a/tests/JMS/Serializer/Tests/Serializer/xml/blog_post.xml +++ b/tests/JMS/Serializer/Tests/Serializer/xml/blog_post.xml @@ -23,4 +23,10 @@ + + tag1 + + + tag2 + diff --git a/tests/JMS/Serializer/Tests/Serializer/xml/object_with_namespaces_and_list.xml b/tests/JMS/Serializer/Tests/Serializer/xml/object_with_namespaces_and_list.xml new file mode 100644 index 000000000..a3022bbd7 --- /dev/null +++ b/tests/JMS/Serializer/Tests/Serializer/xml/object_with_namespaces_and_list.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + +
+
+ + + + + + + + + +
+
+
+
diff --git a/tests/JMS/Serializer/Tests/Serializer/yml/blog_post.yml b/tests/JMS/Serializer/Tests/Serializer/yml/blog_post.yml index 5f03b96a9..fd557c18f 100644 --- a/tests/JMS/Serializer/Tests/Serializer/yml/blog_post.yml +++ b/tests/JMS/Serializer/Tests/Serializer/yml/blog_post.yml @@ -19,3 +19,8 @@ author: full_name: 'Foo Bar' publisher: pub_name: 'Bar Foo' +tag: + - + name: tag1 + - + name: tag2 diff --git a/tests/JMS/Serializer/Tests/Serializer/yml/blog_post_unauthored.yml b/tests/JMS/Serializer/Tests/Serializer/yml/blog_post_unauthored.yml index 76b05d70f..5e080297a 100644 --- a/tests/JMS/Serializer/Tests/Serializer/yml/blog_post_unauthored.yml +++ b/tests/JMS/Serializer/Tests/Serializer/yml/blog_post_unauthored.yml @@ -9,3 +9,4 @@ metadata: foo: bar author: null publisher: null +tag: null