The XML formatter is serializing array paramters with the attribute: is_array="true"

and child elements of that parameter are being wrapped in a <item> </item> element.

The resulting format of an request, for example, for a parameter named "array_parameter" is:

<array_parameter is_array="true">
     <item>
          <contents>example contents 1</contents>
     </item>
     <item>
          <contents>example contents 2</contents>
     </item>
     <item>
          <contents>example contents 3</contents>
     </item>
</array_parameter>

I understand that this may be the correct format when integrating with the Drupal Services module, however it is not the correct format for many other types of web services.

This functionality should either be removed, or there should be an option to enable it. By default, the request format should look like this:

<array_parameter>
     <contents>example contents 1</contents>
     <contents>example contents 2</contents>
     <contents>example contents 3</contents>
</array_parameter>

This problem derives from the xml_recurse function from within HTTP Client.

This is the code from /http_client/includes/HttpClientXMLFormatter.inc Lines 116 - 144 that contains the xml_recurse function:

  protected function xml_recurse(&$doc, &$parent, $data) {
    if (is_object($data)) {
      $data = get_object_vars($data);
    }

    if (is_array($data)) {
      $assoc = FALSE || empty($data);
      foreach ($data as $key => $value) {
        if (is_numeric($key)) {
          $key = 'item';
        }
        else {
          $assoc = TRUE;
          $key = $this->sanitizeNodeName($key);
        }
        $element = $doc->createElement($key);
        $parent->appendChild($element);
        $this->xml_recurse($doc, $element, $value);
      }

      if (!$assoc) {
        $parent->setAttribute('is_array', 'true');
      }
    }
    else if ($data !== NULL) {
      $parent->appendChild($doc->createTextNode($data));
    }
  }
}

Comments

freddura’s picture

Modified the code referenced above from /http_client/includes/HttpClientXMLFormatter.inc Lines 116 - 144, effectively solving the issue. Also added some comments to clarify what is happening:

  /**
   * Stolen from http_server by Hugo Wetterberg
   * Modified by freddura
   */
  protected function xml_recurse(&$doc, &$parent, $data) {
    if (is_object($data)) {
      $data = get_object_vars($data);
    }

    if (is_array($data)) {
      $assoc = FALSE || empty($data);
      foreach ($data as $key => $value) {
        if (is_numeric($key)) {
          // Run the xml_recurse function, however pass the $parent variable in
          // as the element to which any newly created elements will be appended.
          $this->xml_recurse($doc, $parent, $value);
        }
        else {
          $assoc = TRUE;
          // Removed unwanted characters from the key
          $key = $this->sanitizeNodeName($key);
          // Create the new element
          $element = $doc->createElement($key);
          // Append the new element to the parent
          $parent->appendChild($element);
          // Run the xml_recurse function, passing in the $element variable in
          // as the element to which any newly created elements will be appended.
          $this->xml_recurse($doc, $element, $value);
        }
      }
    }
    else if ($data !== NULL) {
      $parent->appendChild($doc->createTextNode($data));
    }
  }
}

With this new code, the XML formatter is no longer serializing array elements with the attribute: is_array="true" nor wrapping its child elements in an <item> </item> element. This resulting format of the request now looks as desired:

<array_parameter>
     <contents>example contents 1</contents>
     <contents>example contents 2</contents>
     <contents>example contents 3</contents>
</array_parameter>
freddura’s picture

Made some improvements to the code to support the use of attributes, see below:

  /**
   * Stolen from http_server by Hugo Wetterberg
   * Modified by freddura
   */
  protected function xml_recurse(&$doc, &$parent, $data) {
    if (is_object($data)) {
      $data = get_object_vars($data);
    }

    if (is_array($data)) {
      $assoc = FALSE || empty($data);
      foreach ($data as $key => $value) {
        if (is_numeric($key)) {
          // Run the xml_recurse function, however pass the $parent variable in
          // as the element to which any newly created elements will be appended.
          $this->xml_recurse($doc, $parent, $value);
        }
        else {
          $assoc = TRUE;
          // Removed unwanted characters from the key
          $key = $this->sanitizeNodeName($key);

          // Determine if the key is suppose to be an attribute, denoted by attr_*
          if (stripos($key, 'attr_') !== false) {
            // If so, add the attribute to the parent element
            $parent->setAttribute(substr($key, 5), $value);
          }
          else {
            // Otherwise create the new element
            $element = $doc->createElement($key);
            // Append the new element to the parent
            $parent->appendChild($element);
            // Run the xml_recurse function, passing in the $element variable in
            // as the element to which any newly created elements will be appended.
            $this->xml_recurse($doc, $element, $value);
          }
        }
      }
    }
    else if ($data !== NULL) {
      $parent->appendChild($doc->createTextNode($data));
    }
  }
freddura’s picture

StatusFileSize
new2.1 KB

Here's the patch:

freddura’s picture

Status: Active » Needs review
freddura’s picture

Issue summary: View changes

typo