I have the following class that I have written. Seems to work OK with simple objects that have one level but does not work well when objects have multiple levels (multi-arrays) and the XML is all messed up. Can anyone help me improve this so it will work with any object?
class XMLGenerator
{
function __construct($obj,$root, $element, $fullXML = true) {
$array = $this->object_2_array($obj);
$this->output = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
$this->output .= $this->make($array, $root, $element, $fullXML);
}
//convert objects into arrays
function object_2_array($result){
$array = array();
foreach ($result as $key => $value){
if (is_object($value)){
$array[$key]=XMLGenerator::object_2_array($value);
} elseif (is_array($value)){
$array[$key]=XMLGenerator::object_2_array($value);
} else {
$array[$key]=$value;
}
}
return $array;
}
//make XML
function make($array, $root, $element, $fullXML) {
if (is_numeric($root)) {
$xml = "<{$element}>\n";
} else {
$xml = "<{$root}>\n";
}
foreach ($array as $key => $value) {
if (is_array($value)) {
if ($element == 'options'){ //workaround for orders 3 level problem, need to rethink this - LT
$xml .= $this->make($value, $key, $key, $fullXML);
} else {
$xml .= $this->make($value, $element, $key, $fullXML);
}
} else {
//any fields with HTML need wrapping in CDATA
if (($key === 'Description')||( $key === 'productDescription' )){
$value = '<![CDATA['. $value .']]>';
//remove any chars XML doesnt like
} else {
$value = htmlentities($value,ENT_QUOTES, 'UTF-8');
$value = functions::xml_entities($value);
}
//decide on node name
if (is_numeric($key)) {
$xml .= "<{$root}>{$value}</{$root}>\n";
} else {
$xml .= "<{$key}>{$value}</{$key}>\n";
}
}
}
if (is_numeric($root)) {
$xml .= "</{$element}>\n";
} else {
$xml .= "</{$root}>\n";
}
return $xml;
}
//save XML to file
function saveFile($file){
//create DOM to ensure all XML is valid before writing to file
$doc = new DOMDocument();
$doc->loadXML($this->output);
if ($doc->save("$file")){
return TRUE;
} else {
return FALSE;
}
}
}
The below is a simple object that works well with the above class.
Products Object ( [db_connection:protected] => 3779074 [prod_id:protected] => 0 [shopkeeper:protected] => 0 [fields] => Array (
[productDescription] => Test [productName] => Test [UPC] => 123 ))
The below does not work well at all.
Order Object ( [db_connection:protected] => msSqlConnect Object ( [con] => [dbName] => )
[skID:protected] => 89137 [orderID:protected] => 482325 [order] => Array ( [id] => 482325 [customer] => 491936 [net] => 1565.98 [vat] => 274.05 [billing_address] => Address Object ( [db_connection:protected] => msSqlConnect Object ( [con] => [dbName] => ) [custID:protected] => 491936 [addID:protected] => 156928 [fields] => Array ( [id] => 156928 [surname] => test [forename] => test [add1] => 89 testRoad [add2] => [city] => City [country] => GB [postcode] => POSTCODE ) ) [items] => Array ( [0] => Array ( [id] => 716549 [headerID] => 482325 [productID] => 4084620 [net] => 22.99 [vat] => 4.0233 [qty] => 1 [options] => Array ( [0] => Array ( [id] => [orderDetailsID] => 716549 [optionid] => 763217 [optionCost] => 100 [optionVAT] => 17.5 ) [1] => Array ( [id] => [orderDetailsID] => 716549 [optionid] => 763241 [optionCost] => 10 [optionVAT] => 1.75 ) ) ) [1] => Array ( [id] => 716551 [headerID] => 482325 [productID] => 3779074 [net] => 1400 [vat] => 245 [qty] => 1 [options] => ) ) ) )
Many thanks in advance for any help.
Multiple levels require a recursive kind of processing - as you don't know the number of levels upfront. While doing the recursion you also need to take care which XML Elements are opened and such.
What you do is that you serialize a PHP object into XML. You're not the first one who needs this, PHP ships with a XML serializer that follows the WDDX specification, for example with the wddx_serialize_value function:
$object = (object) array('hello' => (object) array('value' => 'world') );
echo wddx_serialize_value($object);
Which will give this XML (Demo):
<wddxPacket version='1.0'>
<header/>
<data>
<struct>
<var name='php_class_name'>
<string>stdClass</string>
</var>
<var name='hello'>
<struct>
<var name='php_class_name'>
<string>stdClass</string>
</var>
<var name='value'>
<string>world</string>
</var>
</struct>
</var>
</struct>
</data>
</wddxPacket>
If you need a different output, you need to write the serialization on your own. Within symfony2 (Symfony2 Serializer Component) and Pear (XML_Serializer) you find existing PHP code that does serialization with XML output.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With