Friday, November 16, 2012

PHP Objects to XML

What worked for me was to populate object properties with other objects that have one or more of their own properties. (Credit goes to the venerable James Thompson for pointing this out to me.)

<?php

//So first we need an object of objects.

class rootObject {
    public $root;

    public function populateRoot() {
        $root=new singleFirstTierObject();
        $root->populateSingleFirstTier();
        $this->root=$root;
    }
    public function toXML() {
        $serializer = new XMLSerializer();
        return  $serializer->objToXML($this);
    }
}

class singleFirstTierObject {
    public $singleFirstTier;
    public $singleOtherFirstTier;

    public function populateSingleFirstTier() {
        $singleFirstTier=new singleSecondTierObject();
        $singleFirstTier->populateSingleSecondTier();
        $this->singleFirstTier=$singleFirstTier;
       
        $singleOtherFirstTier=new multipleSecondTierObject();
        $singleOtherFirstTier->populateSingleSecondTier();
        $this->singleOtherFirstTier=$singleOtherFirstTier;
    }
}

class singleSecondTierObject {
    public $singleSecondTier;

    public function populateSingleSecondTier() {
        $this->singleSecondTier='singleSecondTier';
    }
}

class multipleSecondTierObject {
    public $singleSecondTier;
    public $singleOtherSecondTier=array();

    public function populateSingleSecondTier() {
        $this->singleSecondTier='singleSecondTier';
        $argumentArray=array();
        for($i=0;$i<5;$i++) {
            $arrayComponent=new multipleThirdTier();
            $arrayComponent->populateMultipleThirdTier();
            $argumentArray[]=$arrayComponent;
        }
        $this->singleOtherSecondTier=$argumentArray;
    }
}

class multipleThirdTier {
    public $multipleThirdTier;
       
    public function populateMultipleThirdTier() {
        $this->multipleThirdTier='multipleThirdTier';
    }
}

$object = new rootObject();
$object->populateRoot();

echo "<hr />\n<pre>\$object: <br />\n";
echo print_r($object);
echo "</pre><hr />\n";

//Now we have an object that will translate into XML via the XMLSerializer class below using the toXML function in the rootObject class.

//http://www.akchauhan.com/php-class-for-converting-xml-to-object-and-object-to-xml/
class XMLSerializer {
    private static $xml;

    // Constructor
    public function __construct() {
        $this->xml = new XmlWriter();
        $this->xml->openMemory();
        $this->xml->startDocument('1.0');
        $this->xml->setIndent(true);
    }

    // Method to convert Object into XML string
    public function objToXML($obj) {


        $this->getObject2XML($this->xml, $obj);

        $this->xml->endElement();

        return $this->xml->outputMemory(true);
    }

    // Method to convert XML string into Object
    public function xmlToObj($xmlString) {
        return simplexml_load_string($xmlString);
    }

    private function getObject2XML(XMLWriter $xml, $data) {
        foreach($data as $key => $value) {
            if(is_object($value)) {
                $xml->startElement($key);
                $this->getObject2XML($xml, $value);
                $xml->endElement();
                continue;
            }
            else if(is_array($value)) {
                $this->getArray2XML($xml, $key, $value);
            }

            if (is_string($value)) {
                $xml->writeElement($key, $value);
            }elseif(is_numeric($value)) {
                $xml->writeElement($key, $value);
            }
        }
    }

    private function getArray2XML(XMLWriter $xml, $keyParent, $data) {
        foreach($data as $key => $value) {
            if (is_string($value)) {
                $xml->writeElement($keyParent, $value);
                continue;
            }

            if (is_numeric($key)) {
                $xml->startElement($keyParent);
            }

            if(is_object($value)) {
                $this->getObject2XML($xml, $value);
            }
            else if(is_array($value)) {
                $this->getArray2XML($xml, $key, $value);
                continue;
            }

            if (is_numeric($key)) {
                $xml->endElement();
            }
        }
    }
}

$xmlOutput1=$object->toXML();

echo "<hr />\n<pre>\$xmlOutput1: <br />\n";
echo $xmlOutput1;
echo "</pre><hr />\n";

//What if we want to add attributes or edit the XML object though?

$xmlObject = new SimpleXMLElement($xmlOutput1);

echo "<hr />\n<pre>\$xmlObject: <br />\n";
echo "Round 1: <br />\n";
echo print_r($xmlObject);
echo "</pre><hr />\n";

//$xmlObject->singleOtherFirstTier->singleOtherSecondTier[$child_count]->multipleThirdTier

$child_count=0;
foreach($xmlObject->singleOtherFirstTier->singleOtherSecondTier as $element){
    $xmlObject->singleOtherFirstTier->singleOtherSecondTier[$child_count]->multipleThirdTier->addAttribute('child-element',$child_count);
    $child_count++;
}

echo "<hr />\n<pre>\$xmlObject: <br />\n";
echo "Round 2: with new attributes <br />\n";
echo print_r($xmlObject);
echo "</pre><hr />\n";

//Sorry, I know you looked. But, you won't be able to see they are there. And what is worse if you want to access them similarly now that they exist, the attribute name will need encapsulated in {curly braces}.

//And we convert our SimpleXML object back into an XML string.

$xmlOutput2=$xmlObject->asXML();

echo "<hr />\n<pre>\$xmlOutput2: <br />\n";
echo $xmlOutput2;
echo "</pre><hr />\n";

//Also, whitespace is a waste of bandwidth if you are talking to a machine.
//The following code will enable us to fine tune how we want whitespace handled by converting our data in an XML string into a DOM document object.

$domObject = new DOMDocument('1.0');
$domObject->preserveWhiteSpace = false; //self explanatory
   
$domObject->formatOutput = true; //false = no whitespace, true = human friendly tabbed indenting

//Now that we have a DOM document object with the settings we like let's load the XML string into it.
$domObject->loadXML($xmlOutput2);

//Last thing to do is output our finished DOM document object as XML document using the saveXML method.

echo "<hr />\n<pre>\$domObject->saveXML(): <br />\n";
echo $domObject->saveXML();
echo "</pre><hr />\n";

?>
<hr />
<pre>$object: <br />
rootObject Object
(
    [root] => singleFirstTierObject Object
        (
            [singleFirstTier] => singleSecondTierObject Object
                (
                    [singleSecondTier] => singleSecondTier
                )

            [singleOtherFirstTier] => multipleSecondTierObject Object
                (
                    [singleSecondTier] => singleSecondTier
                    [singleOtherSecondTier] => Array
                        (
                            [0] => multipleThirdTier Object
                                (
                                    [multipleThirdTier] => multipleThirdTier
                                )

                            [1] => multipleThirdTier Object
                                (
                                    [multipleThirdTier] => multipleThirdTier
                                )

                            [2] => multipleThirdTier Object
                                (
                                    [multipleThirdTier] => multipleThirdTier
                                )

                            [3] => multipleThirdTier Object
                                (
                                    [multipleThirdTier] => multipleThirdTier
                                )

                            [4] => multipleThirdTier Object
                                (
                                    [multipleThirdTier] => multipleThirdTier
                                )

                        )

                )

        )

)
1</pre><hr />
<hr />
<pre>$xmlOutput1: <br />
<?xml version="1.0"?>
<root>
 <singleFirstTier>
  <singleSecondTier>singleSecondTier</singleSecondTier>
 </singleFirstTier>
 <singleOtherFirstTier>
  <singleSecondTier>singleSecondTier</singleSecondTier>
  <singleOtherSecondTier>
   <multipleThirdTier>multipleThirdTier</multipleThirdTier>
  </singleOtherSecondTier>
  <singleOtherSecondTier>
   <multipleThirdTier>multipleThirdTier</multipleThirdTier>
  </singleOtherSecondTier>
  <singleOtherSecondTier>
   <multipleThirdTier>multipleThirdTier</multipleThirdTier>
  </singleOtherSecondTier>
  <singleOtherSecondTier>
   <multipleThirdTier>multipleThirdTier</multipleThirdTier>
  </singleOtherSecondTier>
  <singleOtherSecondTier>
   <multipleThirdTier>multipleThirdTier</multipleThirdTier>
  </singleOtherSecondTier>
 </singleOtherFirstTier>
</root>
</pre><hr />
<hr />
<pre>$xmlObject: <br />
Round 1: <br />
SimpleXMLElement Object
(
    [singleFirstTier] => SimpleXMLElement Object
        (
            [singleSecondTier] => singleSecondTier
        )

    [singleOtherFirstTier] => SimpleXMLElement Object
        (
            [singleSecondTier] => singleSecondTier
            [singleOtherSecondTier] => Array
                (
                    [0] => SimpleXMLElement Object
                        (
                            [multipleThirdTier] => multipleThirdTier
                        )

                    [1] => SimpleXMLElement Object
                        (
                            [multipleThirdTier] => multipleThirdTier
                        )

                    [2] => SimpleXMLElement Object
                        (
                            [multipleThirdTier] => multipleThirdTier
                        )

                    [3] => SimpleXMLElement Object
                        (
                            [multipleThirdTier] => multipleThirdTier
                        )

                    [4] => SimpleXMLElement Object
                        (
                            [multipleThirdTier] => multipleThirdTier
                        )

                )

        )

)
1</pre><hr />
<hr />
<pre>$xmlObject: <br />
Round 2: with new attributes <br />
SimpleXMLElement Object
(
    [singleFirstTier] => SimpleXMLElement Object
        (
            [singleSecondTier] => singleSecondTier
        )

    [singleOtherFirstTier] => SimpleXMLElement Object
        (
            [singleSecondTier] => singleSecondTier
            [singleOtherSecondTier] => Array
                (
                    [0] => SimpleXMLElement Object
                        (
                            [multipleThirdTier] => multipleThirdTier
                        )

                    [1] => SimpleXMLElement Object
                        (
                            [multipleThirdTier] => multipleThirdTier
                        )

                    [2] => SimpleXMLElement Object
                        (
                            [multipleThirdTier] => multipleThirdTier
                        )

                    [3] => SimpleXMLElement Object
                        (
                            [multipleThirdTier] => multipleThirdTier
                        )

                    [4] => SimpleXMLElement Object
                        (
                            [multipleThirdTier] => multipleThirdTier
                        )

                )

        )

)
1</pre><hr />
<hr />
<pre>$xmlOutput2: <br />
<?xml version="1.0"?>
<root>
 <singleFirstTier>
  <singleSecondTier>singleSecondTier</singleSecondTier>
 </singleFirstTier>
 <singleOtherFirstTier>
  <singleSecondTier>singleSecondTier</singleSecondTier>
  <singleOtherSecondTier>
   <multipleThirdTier child-element="0">multipleThirdTier</multipleThirdTier>
  </singleOtherSecondTier>
  <singleOtherSecondTier>
   <multipleThirdTier child-element="1">multipleThirdTier</multipleThirdTier>
  </singleOtherSecondTier>
  <singleOtherSecondTier>
   <multipleThirdTier child-element="2">multipleThirdTier</multipleThirdTier>
  </singleOtherSecondTier>
  <singleOtherSecondTier>
   <multipleThirdTier child-element="3">multipleThirdTier</multipleThirdTier>
  </singleOtherSecondTier>
  <singleOtherSecondTier>
   <multipleThirdTier child-element="4">multipleThirdTier</multipleThirdTier>
  </singleOtherSecondTier>
 </singleOtherFirstTier>
</root>
</pre><hr />
<hr />
<pre>$domObject->saveXML(): <br />
<?xml version="1.0"?>
<root>
  <singleFirstTier>
    <singleSecondTier>singleSecondTier</singleSecondTier>
  </singleFirstTier>
  <singleOtherFirstTier>
    <singleSecondTier>singleSecondTier</singleSecondTier>
    <singleOtherSecondTier>
      <multipleThirdTier child-element="0">multipleThirdTier</multipleThirdTier>
    </singleOtherSecondTier>
    <singleOtherSecondTier>
      <multipleThirdTier child-element="1">multipleThirdTier</multipleThirdTier>
    </singleOtherSecondTier>
    <singleOtherSecondTier>
      <multipleThirdTier child-element="2">multipleThirdTier</multipleThirdTier>
    </singleOtherSecondTier>
    <singleOtherSecondTier>
      <multipleThirdTier child-element="3">multipleThirdTier</multipleThirdTier>
    </singleOtherSecondTier>
    <singleOtherSecondTier>
      <multipleThirdTier child-element="4">multipleThirdTier</multipleThirdTier>
    </singleOtherSecondTier>
  </singleOtherFirstTier>
</root>
</pre><hr />

 

Are there more compact and elegant ways to do this? Sure. But, this gets the job done.

Followers