Pārlūkot izejas kodu

Add an XmlAttributeMap

Adrien Brault 12 gadi atpakaļ
vecāks
revīzija
5924b2e24d

+ 27 - 0
Annotation/XmlAttributeMap.php

@@ -0,0 +1,27 @@
+<?php
+
+/*
+ * Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
+ *
+ * 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\Annotation;
+
+/**
+ * @Annotation
+ * @Target({"PROPERTY", "METHOD"})
+ */
+final class XmlAttributeMap
+{
+}

+ 3 - 0
Metadata/Driver/AnnotationDriver.php

@@ -55,6 +55,7 @@ use JMS\SerializerBundle\Annotation\ReadOnly;
 use JMS\SerializerBundle\Metadata\ClassMetadata;
 use JMS\SerializerBundle\Metadata\PropertyMetadata;
 use JMS\SerializerBundle\Metadata\VirtualPropertyMetadata;
+use JMS\SerializerBundle\Annotation\XmlAttributeMap;
 use Metadata\Driver\DriverInterface;
 
 class AnnotationDriver implements DriverInterface
@@ -176,6 +177,8 @@ class AnnotationDriver implements DriverInterface
                         $propertyMetadata->groups = $annot->groups;
                     } else if ($annot instanceof Inline) {
                         $propertyMetadata->inline = true;
+                    } else if ($annot instanceof XmlAttributeMap) {
+                        $propertyMetadata->xmlAttributeMap = true;
                     }
                 }
 

+ 4 - 0
Metadata/Driver/XmlDriver.php

@@ -160,6 +160,10 @@ class XmlDriver extends AbstractFileDriver
                         $pMetadata->xmlAttribute = 'true' === (string) $pElem->attributes()->{'xml-attribute'};
                     }
 
+                    if (isset($pElem->attributes()->{'xml-attribute-map'})) {
+                        $pMetadata->xmlAttribute = 'true' === (string) $pElem->attributes()->{'xml-attribute-map'};
+                    }
+
                     if (isset($pElem->attributes()->{'xml-value'})) {
                         $pMetadata->xmlValue = 'true' === (string) $pElem->attributes()->{'xml-value'};
                     }

+ 4 - 0
Metadata/Driver/YamlDriver.php

@@ -151,6 +151,10 @@ class YamlDriver extends AbstractFileDriver
                         $pMetadata->xmlAttribute = (Boolean) $pConfig['xml_attribute'];
                     }
 
+                    if (isset($pConfig['xml_attribute_map'])) {
+                        $pMetadata->xmlAttribute = (Boolean) $pConfig['xml_attribute_map'];
+                    }
+
                     if (isset($pConfig['xml_value'])) {
                         $pMetadata->xmlValue = (Boolean) $pConfig['xml_value'];
                     }

+ 3 - 0
Metadata/PropertyMetadata.php

@@ -43,6 +43,7 @@ class PropertyMetadata extends BasePropertyMetadata
     public $setter;
     public $inline = false;
     public $readOnly = false;
+    public $xmlAttributeMap = false;
 
     private static $typeParser;
 
@@ -102,6 +103,7 @@ class PropertyMetadata extends BasePropertyMetadata
             $this->setter,
             $this->inline,
             $this->readOnly,
+            $this->xmlAttributeMap,
             parent::serialize(),
         ));
     }
@@ -125,6 +127,7 @@ class PropertyMetadata extends BasePropertyMetadata
             $this->setter,
             $this->inline,
             $this->readOnly,
+            $this->xmlAttributeMap,
             $parentStr
         ) = unserialize($str);
 

+ 26 - 0
Resources/doc/reference/annotations.rst

@@ -418,3 +418,29 @@ This allows you to use the keys of an array as xml tags.
 .. note ::
 
     When a key is an invalid xml tag name (e.g. 1_foo) the tag name *entry* will be used instead of the key.
+
+@XmlAttributeMap
+~~~~~~~~~~~~~
+
+This is similar to the @XmlKeyValuePairs, but instead of creating child elements, it creates attributes.
+
+.. code-block :: php
+
+    <?php
+
+    use JMS\SerializerBundle\Annotation\XmlAttribute;
+
+    class Input
+    {
+        /** @XmlAttributeMap */
+        private $id = array(
+            'name' => 'firstname',
+            'value' => 'Adrien',
+        );
+    }
+
+Resulting XML:
+
+.. code-block :: xml
+
+    <result name="firstname" value="Adrien"/>

+ 1 - 0
Resources/doc/reference/xml_reference.rst

@@ -23,6 +23,7 @@ XML Reference
                       read-only="true"
                       groups="foo,bar"
                       xml-key-value-pairs="true"
+                      xml-attribute-map="true"
             >
                 <!-- You can also specify the type as element which is necessary if
                      your type contains "<" or ">" characters. -->

+ 1 - 0
Resources/doc/reference/yml_reference.rst

@@ -34,6 +34,7 @@ YAML Reference
                     inline: true
                     key_attribute_name: foo
                     entry_name: bar
+                xml_attribute_map: true
 
         handler_callbacks:
             serialization:

+ 17 - 0
Serializer/XmlSerializationVisitor.php

@@ -180,6 +180,23 @@ class XmlSerializationVisitor extends AbstractVisitor
             return;
         }
 
+        if ($metadata->xmlAttributeMap) {
+            if (!is_array($v)) {
+                throw new RuntimeException(sprintf('Unsupported value type for XML attribute map. Expected array but got %s.', gettype($v)));
+            }
+
+            foreach ($v as $key => $value) {
+                $node = $this->navigator->accept($value, null, $this);
+                if (!$node instanceof \DOMCharacterData) {
+                    throw new RuntimeException(sprintf('Unsupported value for a XML attribute map value. Expected character data, but got %s.', json_encode($v)));
+                }
+
+                $this->currentNode->setAttribute($key, $node->nodeValue);
+            }
+
+            return;
+        }
+
         if ($addEnclosingElement = (!$metadata->xmlCollection || !$metadata->xmlCollectionInline) && !$metadata->inline) {
             $element = $this->document->createElement($this->namingStrategy->translateName($metadata));
             $this->setCurrentNode($element);

+ 25 - 0
Tests/Fixtures/Input.php

@@ -0,0 +1,25 @@
+<?php
+
+namespace JMS\SerializerBundle\Tests\Fixtures;
+
+use JMS\SerializerBundle\Annotation as Serializer;
+
+/**
+ * @Serializer\XmlRoot("input")
+ */
+class Input
+{
+    /**
+     * @Serializer\XmlAttributeMap
+     */
+    private $attributes;
+
+    public function __construct($attributes = null)
+    {
+        $this->attributes = $attributes ?: array(
+            'type' => 'text',
+            'name' => 'firstname',
+            'value' => 'Adrien',
+        );
+    }
+}

+ 6 - 0
Tests/Serializer/BaseSerializationTest.php

@@ -69,6 +69,7 @@ use JMS\SerializerBundle\Tests\Fixtures\Price;
 use JMS\SerializerBundle\Tests\Fixtures\SimpleObject;
 use JMS\SerializerBundle\Tests\Fixtures\SimpleObjectProxy;
 use JMS\SerializerBundle\Tests\Serializer\Fixture\Article;
+use JMS\SerializerBundle\Tests\Fixtures\Input;
 use Metadata\MetadataFactory;
 use Symfony\Component\Form\Form;
 use Symfony\Component\Form\FormError;
@@ -494,6 +495,11 @@ abstract class BaseSerializationTest extends \PHPUnit_Framework_TestCase
         $this->assertEquals('customly_unserialized_value', $object->someProperty);
     }
 
+    public function testInput()
+    {
+        $this->assertEquals($this->getContent('input'), $this->serializer->serialize(new Input(), $this->getFormat()));
+    }
+
     abstract protected function getContent($key);
     abstract protected function getFormat();
 

+ 3 - 1
Tests/Serializer/JsonSerializationTest.php

@@ -80,6 +80,7 @@ class JsonSerializationTest extends BaseSerializationTest
             $outputs['virtual_properties_low'] = '{"low":1}';
             $outputs['virtual_properties_high'] = '{"high":8}';
             $outputs['virtual_properties_all'] = '{"low":1,"high":8}';
+            $outputs['input'] = '{"attributes":{"type":"text","name":"firstname","value":"Adrien"}}';
         }
 
         if (!isset($outputs[$key])) {
@@ -129,4 +130,5 @@ class LinkAddingSubscriber implements EventSubscriberInterface
             array('event' => 'serializer.post_serialize', 'method' => 'onPostSerialize', 'format' => 'json', 'class' => 'JMS\SerializerBundle\Tests\Fixtures\Author'),
         );
     }
-}
+}
+

+ 14 - 0
Tests/Serializer/XmlSerializationTest.php

@@ -35,6 +35,7 @@ use JMS\SerializerBundle\Tests\Fixtures\PersonLocation;
 use JMS\SerializerBundle\Tests\Fixtures\Person;
 use JMS\SerializerBundle\Tests\Fixtures\ObjectWithVirtualXmlProperties;
 use JMS\SerializerBundle\Tests\Fixtures\ObjectWithXmlKeyValuePairs;
+use JMS\SerializerBundle\Tests\Fixtures\Input;
 
 class XmlSerializationTest extends BaseSerializationTest
 {
@@ -141,6 +142,19 @@ class XmlSerializationTest extends BaseSerializationTest
         $this->assertEquals($this->getContent('array_key_values'), $this->serializer->serialize(new ObjectWithXmlKeyValuePairs(), 'xml'));
     }
 
+    /**
+     * @expectedException RuntimeException
+     * @expectedExceptionMessage Unsupported value type for XML attribute map. Expected array but got object
+     */
+    public function testXmlAttributeMapWithoutArray()
+    {
+        $attributes = new \ArrayObject(array(
+            'type' => 'text',
+        ));
+
+        $this->serializer->serialize(new Input($attributes), $this->getFormat());
+    }
+
     /**
      * @param string $key
      */

+ 2 - 0
Tests/Serializer/xml/input.xml

@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<input type="text" name="firstname" value="Adrien"/>

+ 4 - 0
Tests/Serializer/yml/input.yml

@@ -0,0 +1,4 @@
+attributes:
+    type: text
+    name: firstname
+    value: Adrien