I know this is an old question, but lately I had to create complex XML structures.
My approach contains OOP best practices. The idea is to serialize a parent that contains several children and children.
Nodes get names from class names, but you can override the class name with the first parameter when creating an object for serialization.
You can create: a simple node, without child nodes, EntityList and ArrayList. EntityList is a list of objects of one class, but an ArrayList can have different objects.
Each object must extend the abstract class SerializeXmlAbstract to match the first input parameter in the class: Object2xml, method serialize($object, $name = NULL, $prefix = FALSE) .
By default, if you did not specify the second parameter, the root XML node will have the class name for this object. The third parameter indicates whether the root node has a prefix name or not. The prefix is hard-coded as a private property in the Export2xml class.
interface SerializeXml { public function hasAttributes(); public function getAttributes(); public function setAttributes($attribs = array()); public function getNameOwerriden(); public function isNameOwerriden(); } abstract class SerializeXmlAbstract implements SerializeXml { protected $attributes; protected $nameOwerriden; function __construct($name = NULL) { $this->nameOwerriden = $name; } public function getAttributes() { return $this->attributes; } public function getNameOwerriden() { return $this->nameOwerriden; } public function setAttributes($attribs = array()) { $this->attributes = $attribs; } public function hasAttributes() { return (is_array($this->attributes) && count($this->attributes) > 0) ? TRUE : FALSE; } public function isNameOwerriden() { return $this->nameOwerriden != NULL ? TRUE : FALSE; } } abstract class Entity_list extends SplObjectStorage { protected $_listItemType; public function __construct($type) { $this->setListItemType($type); } private function setListItemType($param) { $this->_listItemType = $param; } public function detach($object) { if ($object instanceOf $this->_listItemType) { parent::detach($object); } } public function attach($object, $data = null) { if ($object instanceOf $this->_listItemType) { parent::attach($object, $data); } } } abstract class Array_list extends SerializeXmlAbstract { protected $_listItemType; protected $_items; public function __construct() { //$this->setListItemType($type); $this->_items = new SplObjectStorage(); } protected function setListItemType($param) { $this->_listItemType = $param; } public function getArray() { $return = array(); $this->_items->rewind(); while ($this->_items->valid()) { $return[] = $this->_items->current(); $this->_items->next(); } // print_r($return); return $return; } public function detach($object) { if ($object instanceOf $this->_listItemType) { if (in_array($this->_items->contains($object))) { $this->_items->detach($object); } } } public function attachItem($ob) { $this->_items->attach($ob); } } class Object2xml { public $rootPrefix = "ernm"; private $addPrefix; public $xml; public function serialize($object, $name = NULL, $prefix = FALSE) { if ($object instanceof SerializeXml) { $this->xml = new DOMDocument('1.0', 'utf-8'); $this->xml->appendChild($this->object2xml($object, $name, TRUE)); $this->xml->formatOutput = true; echo $this->xml->saveXML(); } else { die("Not implement SerializeXml interface"); } } protected function object2xml(SerializeXmlAbstract $object, $nodeName = NULL, $prefix = null) { $single = property_exists(get_class($object), "value"); $nName = $nodeName != NULL ? $nodeName : get_class($object); if ($prefix) { $nName = $this->rootPrefix . ":" . $nName; } if ($single) { $ref = $this->xml->createElement($nName); } elseif (is_object($object)) { if ($object->isNameOwerriden()) { $nodeName = $object->getNameOwerriden(); } $ref = $this->xml->createElement($nName); if ($object->hasAttributes()) { foreach ($object->getAttributes() as $key => $value) { $ref->setAttribute($key, $value); } } foreach (get_object_vars($object) as $n => $prop) { switch (gettype($prop)) { case "object": if ($prop instanceof SplObjectStorage) { $ref->appendChild($this->handleList($n, $prop)); } elseif ($prop instanceof Array_list) { $node = $this->object2xml($prop); foreach ($object->ResourceGroup->getArray() as $key => $value) { $node->appendChild($this->object2xml($value)); } $ref->appendChild($node); } else { $ref->appendChild($this->object2xml($prop)); } break; default : if ($prop != null) { $ref->appendChild($this->xml->createElement($n, $prop)); } break; } } } elseif (is_array($object)) { foreach ($object as $value) { $ref->appendChild($this->object2xml($value)); } } return $ref; } private function handleList($name, SplObjectStorage $param, $nodeName = NULL) { $lst = $this->xml->createElement($nodeName == NULL ? $name : $nodeName); $param->rewind(); while ($param->valid()) { if ($param->current() != null) { $lst->appendChild($this->object2xml($param->current())); } $param->next(); } return $lst; } }
This is the code you need to get valid xml from objects. The following sample creates this xml:
<InsertMessage priority="high"> <NodeSimpleValue firstAttrib="first" secondAttrib="second">simple value</NodeSimpleValue> <Arrarita> <Title>PHP OOP is great</Title> <SequenceNumber>1</SequenceNumber> <Child> <FirstChild>Jimmy</FirstChild> </Child> <Child2> <FirstChild>bird</FirstChild> </Child2> </Arrarita> <ThirdChild> <NodeWithChilds> <FirstChild>John</FirstChild> <ThirdChild>James</ThirdChild> </NodeWithChilds> <NodeWithChilds> <FirstChild>DomDocument</FirstChild> <SecondChild>SplObjectStorage</SecondChild> </NodeWithChilds> </ThirdChild> </InsertMessage>
The classes needed for this xml are:
class NodeWithArrayList extends Array_list { public $Title; public $SequenceNumber; public function __construct($name = NULL) { echo $name; parent::__construct($name); } } class EntityListNode extends Entity_list { public function __construct($name = NULL) { parent::__construct($name); } } class NodeWithChilds extends SerializeXmlAbstract { public $FirstChild; public $SecondChild; public $ThirdChild; public function __construct($name = NULL) { parent::__construct($name); } } class NodeSimpleValue extends SerializeXmlAbstract { protected $value; public function getValue() { return $this->value; } public function setValue($value) { $this->value = $value; } public function __construct($name = NULL) { parent::__construct($name); } }
And finally, the code that creates the object objects:
$firstChild = new NodeSimpleValue("firstChild"); $firstChild->setValue( "simple value" ); $firstChild->setAttributes(array("firstAttrib" => "first", "secondAttrib" => "second")); $secondChild = new NodeWithArrayList("Arrarita"); $secondChild->Title = "PHP OOP is great"; $secondChild->SequenceNumber = 1; $firstListItem = new NodeWithChilds(); $firstListItem->FirstChild = "John"; $firstListItem->ThirdChild = "James"; $firstArrayItem = new NodeWithChilds("Child"); $firstArrayItem->FirstChild = "Jimmy"; $SecondArrayItem = new NodeWithChilds("Child2"); $SecondArrayItem->FirstChild = "bird"; $secondListItem = new NodeWithChilds(); $secondListItem->FirstChild = "DomDocument"; $secondListItem->SecondChild = "SplObjectStorage"; $secondChild->attachItem($firstArrayItem); $secondChild->attachItem($SecondArrayItem); $list = new EntityListNode("NodeWithChilds"); $list->attach($firstListItem); $list->attach($secondListItem); $message = New NodeWithChilds("InsertMessage"); $message->setAttributes(array("priority" => "high")); $message->FirstChild = $firstChild; $message->SecondChild = $secondChild; $message->ThirdChild = $list; $object2xml = new Object2xml(); $object2xml->serialize($message, "xml", TRUE);
Hope this helps someone.
Cheers, Siniša