Bläddra i källkod

Merge branch 'master' of https://github.com/Spea/JMSSerializerBundle

Johannes M. Schmitt 13 år sedan
förälder
incheckning
69b15778dd

+ 27 - 0
Annotation/XmlKeyValuePairs.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 XmlKeyValuePairs
+{
+}

+ 4 - 1
Metadata/Driver/AnnotationDriver.php

@@ -29,6 +29,7 @@ use JMS\SerializerBundle\Annotation\XmlRoot;
 use JMS\SerializerBundle\Annotation\XmlAttribute;
 use JMS\SerializerBundle\Annotation\XmlList;
 use JMS\SerializerBundle\Annotation\XmlValue;
+use JMS\SerializerBundle\Annotation\XmlKeyValuePairs;
 use JMS\SerializerBundle\Annotation\PostSerialize;
 use JMS\SerializerBundle\Annotation\PostDeserialize;
 use JMS\SerializerBundle\Annotation\PreSerialize;
@@ -149,6 +150,8 @@ class AnnotationDriver implements DriverInterface
                         $propertyMetadata->xmlCollectionInline = $annot->inline;
                         $propertyMetadata->xmlEntryName = $annot->entry;
                         $propertyMetadata->xmlKeyAttribute = $annot->keyAttribute;
+                    } else if ($annot instanceof XmlKeyValuePairs) {
+                        $propertyMetadata->xmlKeyValuePairs = true;
                     } else if ($annot instanceof XmlAttribute) {
                         $propertyMetadata->xmlAttribute = true;
                     } else if ($annot instanceof XmlValue) {
@@ -177,4 +180,4 @@ class AnnotationDriver implements DriverInterface
 
         return $classMetadata;
     }
-}
+}

+ 3 - 0
Metadata/PropertyMetadata.php

@@ -36,6 +36,7 @@ class PropertyMetadata extends BasePropertyMetadata
     public $xmlKeyAttribute;
     public $xmlAttribute = false;
     public $xmlValue = false;
+    public $xmlKeyValuePairs = false;
     public $getter;
     public $setter;
     public $inline = false;
@@ -83,6 +84,7 @@ class PropertyMetadata extends BasePropertyMetadata
             $this->xmlKeyAttribute,
             $this->xmlAttribute,
             $this->xmlValue,
+            $this->xmlKeyValuePairs,
             $this->getter,
             $this->setter,
             $this->inline,
@@ -105,6 +107,7 @@ class PropertyMetadata extends BasePropertyMetadata
             $this->xmlKeyAttribute,
             $this->xmlAttribute,
             $this->xmlValue,
+            $this->xmlKeyValuePairs,
             $this->getter,
             $this->setter,
             $this->inline,

+ 3 - 1
Metadata/VirtualPropertyMetadata.php

@@ -65,6 +65,7 @@ class VirtualPropertyMetadata extends PropertyMetadata
             $this->xmlKeyAttribute,
             $this->xmlAttribute,
             $this->xmlValue,
+            $this->xmlKeyValuePairs,
             $this->getter,
             $this->setter,
             $this->inline,
@@ -88,6 +89,7 @@ class VirtualPropertyMetadata extends PropertyMetadata
             $this->xmlKeyAttribute,
             $this->xmlAttribute,
             $this->xmlValue,
+            $this->xmlKeyValuePairs,
             $this->getter,
             $this->setter,
             $this->inline,
@@ -96,4 +98,4 @@ class VirtualPropertyMetadata extends PropertyMetadata
             $this->name
         ) = unserialize($str);
     }
-}
+}

+ 29 - 21
Resources/doc/reference/annotations.rst

@@ -48,8 +48,8 @@ PHP's ``version_compare`` function.
 
 @Groups
 ~~~~~~~
-This annotation can be defined on a property to specifiy to if the property 
-should be serialized when only serializing specific groups (see 
+This annotation can be defined on a property to specifiy to if the property
+should be serialized when only serializing specific groups (see
 :doc:`../cookbook/exclusion_strategies`).
 
 @AccessType
@@ -66,12 +66,12 @@ set the value via reflection, but you may change this to use a public method ins
     class User
     {
         private $name;
-        
+
         public function getName()
         {
             return $this->name;
         }
-        
+
         public function setName($name)
         {
             $this->name = trim($name);
@@ -90,16 +90,16 @@ be called to retrieve, or set the value of the given property:
     class User
     {
         private $id;
-        
+
         /** @Accessor(getter="getTrimmedName") */
         private $name;
-        
+
         // ...
         public function getTrimmedName()
         {
             return trim($this->name);
         }
-        
+
         public function setName($name)
         {
             $this->name = $name;
@@ -108,17 +108,17 @@ be called to retrieve, or set the value of the given property:
 
 @AccessorOrder
 ~~~~~~~~~~~~~~
-This annotation can be defined on a class to control the order of properties. By 
+This annotation can be defined on a class to control the order of properties. By
 default the order is undefined, but you may change it to either "alphabetical", or
 "custom".
 
 .. code-block :: php
-    
+
     <?php
 
-    /** 
-     * @AccessorOrder("alphabetical") 
-     * 
+    /**
+     * @AccessorOrder("alphabetical")
+     *
      * Resulting Property Order: id, name
      */
     class User
@@ -126,7 +126,7 @@ default the order is undefined, but you may change it to either "alphabetical",
         private $id;
         private $name;
     }
-    
+
     /**
      * @AccessorOrder("custom", custom = {"name", "id"})
      *
@@ -141,7 +141,7 @@ default the order is undefined, but you may change it to either "alphabetical",
 @Inline
 ~~~~~~~~
 This annotation can be defined on a property to indicate that the data of the property
-should be inlined. 
+should be inlined.
 
 **Note**: This only works for serialization, the serializer will not be able to deserialize
 objects with this annotation. Also, AccessorOrder will be using the name of the property
@@ -299,31 +299,31 @@ Resulting XML:
     <result id="1">
         <name><![CDATA[Johannes]]></name>
     </result>
-    
+
 @XmlValue
 ~~~~~~~~~
 This allows you to mark properties which should be set as the value of the
-current element. Note that this has the limitation that any additional 
+current element. Note that this has the limitation that any additional
 properties of that object must have the @XmlAttribute annotation.
 
 .. code-block :: php
 
     <?php
-    
+
     use JMS\SerializerBundle\Annotation\XmlAttribute;
     use JMS\SerializerBundle\Annotation\XmlValue;
     use JMS\SerializerBundle\Annotation\XmlRoot;
-    
+
     /** @XmlRoot("price") */
     class Price
     {
         /** @XmlAttribute */
         private $currency = 'EUR';
-        
+
         /** @XmlValue */
         private $amount = 1.23;
     }
-    
+
 Resulting XML:
 
 .. code-block :: xml
@@ -380,4 +380,12 @@ Resulting XML:
 
 @XmlMap
 ~~~~~~~
-Similar to @XmlList, but the keys of the array are meaningful.
+Similar to @XmlList, but the keys of the array are meaningful.
+
+@XmlKeyValuePairs
+~~~~~~~~~~~~~~~~~
+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.

+ 18 - 1
Serializer/XmlSerializationVisitor.php

@@ -114,7 +114,9 @@ class XmlSerializationVisitor extends AbstractSerializationVisitor
         $keyAttributeName = (null !== $this->currentMetadata && null !== $this->currentMetadata->xmlKeyAttribute) ? $this->currentMetadata->xmlKeyAttribute : null;
 
         foreach ($data as $k => $v) {
-            $entryNode = $this->document->createElement($entryName);
+            $tagName = (null !== $this->currentMetadata && $this->currentMetadata->xmlKeyValuePairs && $this->isElementNameValid($k)) ? $k : $entryName;
+
+            $entryNode = $this->document->createElement($tagName);
             $this->currentNode->appendChild($entryNode);
             $this->setCurrentNode($entryNode);
 
@@ -283,4 +285,19 @@ class XmlSerializationVisitor extends AbstractSerializationVisitor
 
         return $this->document->createTextNode((string) $data);
     }
+
+    /**
+     * Checks the name is a valid xml element name
+     *
+     * @param string $name
+     *
+     * @return Boolean
+     */
+    private function isElementNameValid($name)
+    {
+        return $name &&
+            false === strpos($name, ' ') &&
+            preg_match('#^[\pL_][\pL0-9._-]*$#ui', $name);
+    }
+
 }

+ 30 - 0
Tests/Fixtures/ObjectWithXmlKeyValuePairs.php

@@ -0,0 +1,30 @@
+<?php
+
+namespace JMS\SerializerBundle\Tests\Fixtures;
+
+use JMS\SerializerBundle\Annotation\XmlKeyValuePairs;
+
+class ObjectWithXmlKeyValuePairs
+{
+    /**
+     * @var array
+     * @XmlKeyValuePairs
+     */
+    private $array = array(
+        'key-one' => 'foo',
+        'key-two' => 1,
+        'nested-array' => array(
+            'bar' => 'foo',
+        ),
+        'without-keys' => array(
+            1,
+            'test'
+        ),
+        'mixed' => array(
+            'test',
+            'foo' => 'bar',
+            '1_foo' => 'bar'
+        ),
+        1 => 'foo'
+    );
+}

+ 7 - 0
Tests/Serializer/XmlSerializationTest.php

@@ -26,6 +26,7 @@ use JMS\SerializerBundle\Tests\Fixtures\PersonCollection;
 use JMS\SerializerBundle\Tests\Fixtures\PersonLocation;
 use JMS\SerializerBundle\Tests\Fixtures\Person;
 use JMS\SerializerBundle\Tests\Fixtures\ObjectWithVirtualXmlProperties;
+use JMS\SerializerBundle\Tests\Fixtures\ObjectWithXmlKeyValuePairs;
 
 class XmlSerializationTest extends BaseSerializationTest
 {
@@ -102,6 +103,12 @@ class XmlSerializationTest extends BaseSerializationTest
         $this->assertEquals($this->getContent('virtual_properties_map'), $serializer->serialize(new ObjectWithVirtualXmlProperties(),'xml'));
     }
 
+    public function testArrayKeyValues()
+    {
+        $serializer = $this->getSerializer();
+        $this->assertEquals($this->getContent('array_key_values'), $serializer->serialize(new ObjectWithXmlKeyValuePairs(), 'xml'));
+    }
+
     protected function getContent($key)
     {
         if (!file_exists($file = __DIR__.'/xml/'.$key.'.xml')) {

+ 20 - 0
Tests/Serializer/xml/array_key_values.xml

@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<result>
+  <array>
+    <key-one><![CDATA[foo]]></key-one>
+    <key-two>1</key-two>
+    <nested-array>
+      <bar><![CDATA[foo]]></bar>
+    </nested-array>
+    <without-keys>
+      <entry>1</entry>
+      <entry><![CDATA[test]]></entry>
+    </without-keys>
+    <mixed>
+      <entry><![CDATA[test]]></entry>
+      <foo><![CDATA[bar]]></foo>
+      <entry><![CDATA[bar]]></entry>
+    </mixed>
+    <entry><![CDATA[foo]]></entry>
+  </array>
+</result>