Zend RESTful XML Generic Functions


It has been argued that the Zend RESTful API should not only automate the JSON data returned (which it does a fine job of), but automate the XML data. Unfortunately, the Zend engine doesn’t do this for REST. The solution is left to the developer to implement their own strategy. In my opinion, there is no better place for a simple recursive function that will dynamically crawl the returned objects and arrays and generate XML in a way that just makes sense. As was promised in a comment from one of my previous blogs, here is a set of functions that works well for dynamically generating XML from your PHP objects and arrays. It’s been pointed out that this process should have been included in the Zend framework. As of this writing, I believe that it still has not been implemented. Anyways, here it is. Take note that you really only need two functions from this post getXML(…) and _xmlHelper(…)

abstract class BaseController extends Zend_Rest_Controller
{
//Please know that the init() method has been snipped out.	
 
	public static function getXML($obj)
    {
    	$doc = new DOMDocument();
		$doc->formatOutput = true;
		$root_element = $doc->createElement("response");
		$doc->appendChild($root_element);
 
    	foreach($obj as $var => $value) {
        	$statusElement = $doc->createElement($var);
			if (!is_array($value)) {
				$statusElement->appendChild($doc->createTextNode($value));
				$root_element->appendChild($statusElement);
			} else {
				BaseController::_xmlHelper(&$doc, &$root_element, &$statusElement, &$value);
			}
		}
		print $doc->saveXML();
    }
	public static function _xmlHelper(&$doc, &$root_element, &$statusElement, &$value) {
		if (is_array($value)) {
			foreach ($value as $key => $val) {
				if (is_array($val)) {
					BaseController::_xmlHelper(&$doc, &$root_element, &$statusElement, $val);
				} else if (is_object($val)) {
					$se = $doc->createElement(str_replace('object','',get_class($val)));
					$arr = get_object_vars($val);
					BaseController::_xmlHelper(&$doc, &$root_element, &$se, $arr);
					$root_element->appendChild($se);
 
				} else {
					//print $key . " => " . $val . "\n";
					$se = $doc->createElement($key);
					$se->appendChild($doc->createTextNode($val));
					$statusElement->appendChild($se);
				}
			}
			//print_r($value);
		} else {
			$statusElement->appendChild($doc->createTextNode($value));
			$root_element->appendChild($statusElement);
		}
	}
}

Now the only thing left to do is show an example implementation.

class VersionController extends BaseController 
{ 
/*  implement the methods as show in the blog referenced at the top of this blog post */
public function init()
    {
       $this->view->hello = array('a'=>'1', 'b'=>'2');
    }
}

Now, the last thing to do is edit the view for the XML document which would be in this case:

/application/views/scripts/version/index.xml.phtml

The index.xml.phtml should then only contain:

<?php
BaseController::getXML($this);
?>

This will automate the process of generating the XML data you want returned. It’s an easy way to quickly and effectively get XML without having to rebuild the entire wheel. Feel free to modify and distribute.

, , ,

  1. #1 by Charles on November 24th, 2009

    Hi Chris-

    I was having some problems getting arrays and objects to convert to XML. I made a few changes to your posted code, and just wanted to share it. I also removed the runtime call-by-reference markers from the calls to the _xmlHelper method, as this feature has been deprecated, and results in a warning depending on the error reporting level. Here’s the code, my apologies if I’m violating comment ettiquette by listing it out here:

    public static function getXML($obj)
        {
            $doc = new DOMDocument();
            $doc->formatOutput = true;
            $root_element = $doc->createElement("response");
            $doc->appendChild($root_element);
     
            foreach($obj as $var => $value)
            {
                $statusElement = $doc->createElement($var);
                if (!is_array($value) && !is_object($value))
                {
                    $statusElement->appendChild($doc->createTextNode($value));
                    $root_element->appendChild($statusElement);
                }
                else
                {
                    self::_xmlHelper($doc, $root_element, $statusElement, $value);
                }
            }
            print $doc->saveXML();
        }
     
        public static function _xmlHelper(&$doc, &$root_element, &$statusElement, &$value)
        {
            if (is_array($value))
            {
                foreach ($value as $key => $val)
                {
                    if (is_array($val))
                    {
                        self::_xmlHelper($doc, $root_element, $statusElement, $val);
                    }
                    else if (is_object($val))
                    {
                            $se = $doc->createElement(str_replace('object','',get_class($val)));
                            $arr = get_object_vars($val);
                            self::_xmlHelper($doc, $root_element, $se, $arr);
                            $root_element->appendChild($se);
     
                    }
                    else
                    {
                        //print $key . " => " . $val . "\n";
                            $se = $doc->createElement($key);
                            $se->appendChild($doc->createTextNode($val));
                            $statusElement->appendChild($se);
     
                    }
                }
            $root_element->appendChild($statusElement);
            //print_r($value);
            }
            else if (is_object($value))
            {
                    $se = $doc->createElement(str_replace('object','',get_class($value)));
                    $arr = get_object_vars($value);
                    self::_xmlHelper($doc, $root_element, $se, $arr);
                    $root_element->appendChild($se);
            }
            else
            {
                    $statusElement->appendChild($doc->createTextNode($value));
                    $root_element->appendChild($statusElement);
            }
        }
  2. #2 by Chris Danielson on November 28th, 2009

    Charles,
    My apologies for the delay. I was on vacation with my family. This looks great! I went ahead and put everything into a code highlighted block! Thanks for sharing.
    Regards,
    Chris

  3. #3 by Fabian on November 11th, 2010

    Hi,

    one year past by since you wrote this.
    I have a better idea (which I didn’t tested yet): Zend_Rest_Controller just extends Zend_Controller_Action, so you can have action helpers. If you create an action helper which is parsing your arrays/objects to XML/JSON you don’t need to extend Zend_Rest_Controller, avoid static methods, pass the output direct to the response object without having view scripts and have a nice and clean solution.

  4. #4 by Derek Martin on July 28th, 2011

    I implemented it as a View Helper, so in my xml view I can do $this->xml($this->content); and in my json view I can do: $this->json($this->content); I got the idea from an article by Matthew Weier O’Phinney, at: http://weierophinney.net/matthew/archives/233-Responding-to-Different-Content-Types-in-RESTful-ZF-Apps.html

(will not be published)


  1. No trackbacks yet.