瀏覽代碼

[Serializer] Using DOMElement instead of SimpleXmlElement in XmlEncoder to permit some behavior

Brouznouf 14 年之前
父節點
當前提交
8e6233e9c2

+ 47 - 22
src/Symfony/Component/Serializer/Encoder/XmlEncoder.php

@@ -53,11 +53,13 @@ class XmlEncoder extends AbstractEncoder
      */
     public function decode($data, $format)
     {
-        $xml = simplexml_load_string($data);
-        if (!$xml->count()) {
-            return (string) $xml;
+        $xml = \DOMDocument::loadXML($data);
+        if (!$xml->documentElement->hasChildNodes()) {
+            return "";
+        } elseif ($xml->documentElement->childNodes->length == 1 && $xml->documentElement->firstChild instanceof \DOMText) {
+            return trim((string)$xml->documentElement->firstChild->wholeText);
         }
-        return $this->parseXml($xml);
+        return $this->parseXml($xml->documentElement);
     }
 
     /**
@@ -149,41 +151,62 @@ class XmlEncoder extends AbstractEncoder
     }
 
     /**
-     * Parse the input SimpleXmlElement into an array
+     * Parse the input DOMElement into an array
      *
-     * @param SimpleXmlElement $node xml to parse
+     * @param DOMElement $node xml to parse
      * @return array
      */
     private function parseXml($node)
     {
         $data = array();
-        foreach ($node->children() as $key => $subnode) {
-            if ($subnode->count()) {
+        foreach ($node->childNodes as $subnode) {
+            //When xml is "beautiful" (with tabs and newlines...), tabs and newline are considered as text but we do not want them
+            if ($subnode instanceof DOMText && trim($subnode->wholeText) === "") {
+                continue;
+            }
+            if (!$subnode->hasChildNodes()) {
+                $value = "";
+            } elseif ($subnode->childNodes->length == 1 && $subnode->firstChild instanceof \DOMText) {
+                $value = trim((string)$subnode->firstChild->wholeText);
+            } else {
                 $value = $this->parseXml($subnode);
-                if ($subnode->attributes()) {
-                    foreach ($subnode->attributes() as $attrkey => $attr) {
-                        $value['@'.$attrkey] = (string) $attr;
-                    }
+            }
+            
+            if ($subnode->hasAttributes()) {
+                if (is_string($value) && $value !== "") {
+                    $value = array('#' => $value);
+                } elseif (is_string($value)) {
+                    $value = array();
+                }
+                foreach($subnode->attributes as $attrKey => $attr) {
+                    $value['@'.$attrKey] = (string) $attr->value;
                 }
-            } else {
-                $value = (string) $subnode;
             }
-            if ($key === 'item') {
-                if (isset($subnode['key'])) {
-                    $data[(string)$subnode['key']] = $value;
+            
+            if ($subnode->tagName === 'item') {
+                if (isset($value['@key'])) {
+                    $key = $value['@key'];
+                    $tmp = $value['#'];
+                    unset($value['@key']);
+                    unset($value['#']);
+                    if (!empty($value)) {
+                        $data[$key] = array_merge(array('#' => $tmp), $value);
+                    } else {
+                        $data[$key] = $tmp;
+                    }
                 } elseif (isset($data['item'])) {
                     $tmp = $data['item'];
                     unset($data['item']);
                     $data[] = $tmp;
                     $data[] = $value;
                 }
-            } elseif (key_exists($key, $data)) {
-                if (false === is_array($data[$key])) {
-                    $data[$key] = array($data[$key]);
+            } elseif (key_exists($subnode->tagName, $data)) {
+                if ((false === is_array($data[$subnode->tagName])) || (false === isset($data[$subnode->tagName][0]))) {
+                    $data[$subnode->tagName] = array($data[$subnode->tagName]);
                 }
-                $data[$key][] = $value;
+                $data[$subnode->tagName][] = $value;
             } else {
-                $data[$key] = $value;
+                $data[$subnode->tagName] = $value;
             }
         }
         return $data;
@@ -205,6 +228,8 @@ class XmlEncoder extends AbstractEncoder
                 //Ah this is the magic @ attribute types.
                 if (0 === strpos($key, "@") && is_scalar($data) && $this->isElementNameValid($attributeName = substr($key,1))) {
                     $parentNode->setAttribute($attributeName, $data);
+                } elseif ($key === '#') {
+                    $append = $this->selectNodeType($parentNode, $data);
                 } elseif (is_array($data) && false === is_numeric($key)) {
                     /**
                     * Is this array fully numeric keys?

+ 45 - 1
tests/Symfony/Tests/Component/Serializer/Encoder/XmlEncoderTest.php

@@ -111,6 +111,18 @@ class XmlEncoderTest extends \PHPUnit_Framework_TestCase
 
         $this->assertEquals($expected, $this->encoder->encode($array, 'xml'));
     }
+    
+    public function testEncodeScalarWithAttribute()
+    {
+        $array = array(
+            'person' => array('@gender' => 'M', '#' => 'Peter'),
+        );
+    
+        $expected = '<?xml version="1.0"?>'."\n".
+            '<response><person gender="M"><![CDATA[Peter]]></person></response>'."\n";
+    
+        $this->assertEquals($expected, $this->encoder->encode($array, 'xml'));
+    }
 
     public function testDecodeScalar()
     {
@@ -135,6 +147,38 @@ class XmlEncoderTest extends \PHPUnit_Framework_TestCase
 
         $this->assertEquals(get_object_vars($obj), $this->encoder->decode($source, 'xml'));
     }
+    
+    public function testDecodeScalarWithAttribute()
+    {
+        $source = '<?xml version="1.0"?>'."\n".
+            '<response><person gender="M">Peter</person></response>'."\n";
+      
+        $expected = array(
+            'person' => array('@gender' => 'M', '#' => 'Peter'),
+        );
+      
+        $this->assertEquals($expected, $this->encoder->decode($source, 'xml'));
+    }
+    
+    public function testDecodeArray()
+    {
+        $source = '<?xml version="1.0"?>'."\n".
+            '<response>'.
+            '<people>'.
+            '<person><firstname>Benjamin</firstname><lastname>Alexandre</lastname></person>'.
+            '<person><firstname>Damien</firstname><lastname>Clay</lastname></person>'.
+            '</people>'.
+            '</response>'."\n";
+        
+        $expected = array(
+            'people' => array('person' => array(
+                array('firstname' => 'Benjamin', 'lastname' => 'Alexandre'),
+                array('firstname' => 'Damien', 'lastname' => 'Clay')
+            ))
+        );
+        
+        $this->assertEquals($expected, $this->encoder->decode($source, 'xml'));
+    }
 
     protected function getXmlSource()
     {
@@ -153,7 +197,7 @@ class XmlEncoderTest extends \PHPUnit_Framework_TestCase
         $obj = new Dummy;
         $obj->foo = 'foo';
         $obj->bar = array('a', 'b');
-        $obj->baz = array('key' => 'val', 'key2' => 'val', 'A B' => 'bar', "Barry" => array('FooBar' => array("@id"=>1,"Baz"=>"Ed")));
+        $obj->baz = array('key' => 'val', 'key2' => 'val', 'A B' => 'bar', "Barry" => array('FooBar' => array("Baz"=>"Ed", "@id"=>1)));
         $obj->qux = "1";
         return $obj;
     }