From 851e77b271480730049c68b101a52edd9befad33 Mon Sep 17 00:00:00 2001
From: Hugo Wetterberg <hugo@wetterberg.nu>
Date: Fri, 23 Sep 2011 09:20:04 +0200
Subject: [PATCH] Issue #931250: Providing options to configure the root
 element of XML request documents generated by XML
 Formatter. This is implemented in a backwards-compatible
 way, so existing deployments shouldn't be affected.

---
 includes/HttpClientXMLFormatter.inc |   49 ++++++++++++++++++++++++++++++++--
 1 files changed, 46 insertions(+), 3 deletions(-)

diff --git a/includes/HttpClientXMLFormatter.inc b/includes/HttpClientXMLFormatter.inc
index 0ac5f95..49d4e22 100644
--- a/includes/HttpClientXMLFormatter.inc
+++ b/includes/HttpClientXMLFormatter.inc
@@ -8,6 +8,25 @@
  */
 
 class HttpClientXMLFormatter implements HttpClientFormatter {
+  private $adaptive_root;
+  private $default_root;
+
+  /**
+   * Creates a HttpClientXMLFormatter.
+   *
+   * @param string $default_root
+   *  Optional. Defaults to 'result'. The default name that should be used for root elements,
+   *  if $adaptive_root is set to FALSE the default name will always be used.
+   * @param bool $adaptive_root
+   *  Optional. Defaults to FALSE. If $adaptive_root is set to TRUE and the source data has a
+   *  single root attribute the serializer will use that attribute as root. The object {"foo":"bar"}
+   *  would be serialized to <foo>bar</foo> instead of <result><foo>bar</foo></result>.
+   */
+  public function __construct($default_root = 'result', $adaptive_root = FALSE) {
+    $this->default_root = $default_root;
+    $this->adaptive_root = $adaptive_root;
+  }
+
   /**
    * Serializes arbitrary data to the implemented format.
    * Directly stolen from http_server by Hugo Wetterberg
@@ -19,7 +38,19 @@ class HttpClientXMLFormatter implements HttpClientFormatter {
    */
   public function serialize($data) {
     $doc = new DOMDocument('1.0', 'utf-8');
-    $root = $doc->createElement('result');
+    $root_tag = $this->default_root;
+
+    // Normalize any objects into an array.
+    if (is_object($data)) {
+      $data = get_object_vars($data);
+    }
+    // Check if we should adapt the name of the root element.
+    if ($this->adaptive_root && is_array($data) && (count($data) == 1) && !is_numeric(key($data))) {
+      $root_tag = $this->sanitizeNodeName(key($data));
+      $data = current($data);
+    }
+
+    $root = $doc->createElement($root_tag);
     $doc->appendChild($root);
 
     $this->xml_recurse($doc, $root, $data);
@@ -28,6 +59,19 @@ class HttpClientXMLFormatter implements HttpClientFormatter {
   }
 
   /**
+   * Sanitizes a string so that it's suitable for use as a element
+   * or attribute name.
+   *
+   * @param string $name
+   * @return string
+   *  The sanitized name.
+   */
+  private function sanitizeNodeName($name) {
+    $name = preg_replace('/[^A-Za-z0-9_]/', '_', $name);
+    return preg_replace('/^([0-9]+)/', '_$1', $name);
+  }
+
+  /**
    * Unserializes data in the implemented format.
    *
    * @param string $data
@@ -68,8 +112,7 @@ class HttpClientXMLFormatter implements HttpClientFormatter {
         }
         else {
           $assoc = TRUE;
-          $key = preg_replace('/[^A-Za-z0-9_]/', '_', $key);
-          $key = preg_replace('/^([0-9]+)/', '_$1', $key);
+          $key = $this->sanitizeNodeName($key);
         }
         $element = $doc->createElement($key);
         $parent->appendChild($element);
-- 
1.7.6.1

