PHP Object as XML Document

Viewed 90828

What is the best way to take a given PHP object and serialize it as XML? I am looking at simple_xml and I have used it to parse XML into objects, but it isn't clear to me how it works the other way around.

12 Answers

I'd agree with using PEAR's XML_Serializer, but if you want something simple that supports objects/arrays that have properties nested, you can use this.

class XMLSerializer {

    // functions adopted from http://www.sean-barton.co.uk/2009/03/turning-an-array-or-object-into-xml-using-php/

    public static function generateValidXmlFromObj(stdClass $obj, $node_block='nodes', $node_name='node') {
        $arr = get_object_vars($obj);
        return self::generateValidXmlFromArray($arr, $node_block, $node_name);
    }

    public static function generateValidXmlFromArray($array, $node_block='nodes', $node_name='node') {
        $xml = '<?xml version="1.0" encoding="UTF-8" ?>';

        $xml .= '<' . $node_block . '>';
        $xml .= self::generateXmlFromArray($array, $node_name);
        $xml .= '</' . $node_block . '>';

        return $xml;
    }

    private static function generateXmlFromArray($array, $node_name) {
        $xml = '';

        if (is_array($array) || is_object($array)) {
            foreach ($array as $key=>$value) {
                if (is_numeric($key)) {
                    $key = $node_name;
                }

                $xml .= '<' . $key . '>' . self::generateXmlFromArray($value, $node_name) . '</' . $key . '>';
            }
        } else {
            $xml = htmlspecialchars($array, ENT_QUOTES);
        }

        return $xml;
    }

}

take a look at PEAR's XML_Serializer package. I've used it with pretty good results. You can feed it arrays, objects etc and it will turn them into XML. It also has a bunch of options like picking the name of the root node etc.

Should do the trick

not quite an answer to the original question, but the way i solved my problem with this was by declaring my object as:

$root = '<?xml version="1.0" encoding="UTF-8"?><Activities/>';
$object = new simpleXMLElement($root); 

as opposed to:

$object = new stdClass;

before i started adding any values!

Use a dom function to do it: http://www.php.net/manual/en/function.dom-import-simplexml.php

Import the SimpleXML object and then save. The above link contains an example. :)

In a nutshell:

<?php
$array = array('hello' => 'world', 'good' => 'morning');

$xml = simplexml_load_string("<?xml version='1.0' encoding='utf-8'?><foo />");
foreach ($array as $k=>$v) {
  $xml->addChild($k, $v);
}
?>

Here is my code used for serializing PHP objects to XML "understandable" by Microsoft .NET XmlSerializer.Deserialize

class XMLSerializer {

    /**
     * Get object class name without namespace
     * @param object $object Object to get class name from
     * @return string Class name without namespace
     */
    private static function GetClassNameWithoutNamespace($object) {
        $class_name = get_class($object);
        return end(explode('\\', $class_name));
    }

    /**
     * Converts object to XML compatible with .NET XmlSerializer.Deserialize 
     * @param type $object Object to serialize
     * @param type $root_node Root node name (if null, objects class name is used)
     * @return string XML string
     */
    public static function Serialize($object, $root_node = null) {
        $xml = '<?xml version="1.0" encoding="UTF-8" ?>';
        if (!$root_node) {
            $root_node = self::GetClassNameWithoutNamespace($object);
        }
        $xml .= '<' . $root_node . ' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">';
        $xml .= self::SerializeNode($object);
        $xml .= '</' . $root_node . '>';
        return $xml;
    }

    /**
     * Create XML node from object property
     * @param mixed $node Object property
     * @param string $parent_node_name Parent node name
     * @param bool $is_array_item Is this node an item of an array?
     * @return string XML node as string
     * @throws Exception
     */
    private static function SerializeNode($node, $parent_node_name = false, $is_array_item = false) {
        $xml = '';
        if (is_object($node)) {
            $vars = get_object_vars($node);
        } else if (is_array($node)) {
            $vars = $node;
        } else {
            throw new Exception('Coś poszło nie tak');
        }

        foreach ($vars as $k => $v) {
            if (is_object($v)) {
                $node_name = ($parent_node_name ? $parent_node_name : self::GetClassNameWithoutNamespace($v));
                if (!$is_array_item) {
                    $node_name = $k;
                }
                $xml .= '<' . $node_name . '>';
                $xml .= self::SerializeNode($v);
                $xml .= '</' . $node_name . '>';
            } else if (is_array($v)) {
                $xml .= '<' . $k . '>';
                if (count($v) > 0) {
                    if (is_object(reset($v))) {
                        $xml .= self::SerializeNode($v, self::GetClassNameWithoutNamespace(reset($v)), true);
                    } else {
                        $xml .= self::SerializeNode($v, gettype(reset($v)), true);
                    }
                } else {
                    $xml .= self::SerializeNode($v, false, true);
                }
                $xml .= '</' . $k . '>';
            } else {
                $node_name = ($parent_node_name ? $parent_node_name : $k);
                if ($v === null) {
                    continue;
                } else {
                    $xml .= '<' . $node_name . '>';
                    if (is_bool($v)) {
                        $xml .= $v ? 'true' : 'false';
                    } else {
                        $xml .= htmlspecialchars($v, ENT_QUOTES);
                    }
                    $xml .= '</' . $node_name . '>';
                }
            }
        }
        return $xml;
    }
}

example:

class GetProductsCommandResult {
    public $description;
    public $Errors;
}

class Error {
    public $id;
    public $error;
}

$obj = new GetProductsCommandResult();
$obj->description = "Teścik";
$obj->Errors = array();
$obj->Errors[0] = new Error();
$obj->Errors[0]->id = 666;
$obj->Errors[0]->error = "Sth";
$obj->Errors[1] = new Error();
$obj->Errors[1]->id = 666;
$obj->Errors[1]->error = null;


$xml = XMLSerializer::Serialize($obj);

results in:

<?xml version="1.0" encoding="UTF-8"?>
<GetProductsCommandResult xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <description>Teścik</description>
   <Errors>
      <Error>
         <id>666</id>
         <error>Sth</error>
      </Error>
      <Error>
         <id>666</id>
      </Error>
   </Errors>
</GetProductsCommandResult>

Use recursive method, like this:

private function ReadProperty($xmlElement, $object) {
    foreach ($object as $key => $value) {
        if ($value != null) {
            if (is_object($value)) {
                $element = $this->xml->createElement($key);
                $this->ReadProperty($element, $value);
                $xmlElement->AppendChild($element);
            } elseif (is_array($value)) {
                $this->ReadProperty($xmlElement, $value);
            } else {
                $this->AddAttribute($xmlElement, $key, $value);
            }
        }
    }
}

Here the complete example: http://www.tyrodeveloper.com/2018/09/convertir-clase-en-xml-con-php.html

I recently created a class available via git that resolves this problem:

https://github.com/zappz88/XMLSerializer

Here is the class structure, and keep in mind that you will need to define the root properly for format proper xml:

class XMLSerializer {
    private $OpenTag = "<";
    private $CloseTag = ">";
    private $BackSlash = "/";
    public $Root = "root";

    public function __construct() {
    }

    private function Array_To_XML($array, $arrayElementName = "element_", $xmlString = "")
    {
        if($xmlString === "")
        {
            $xmlString = "{$this->OpenTag}{$this->Root}{$this->CloseTag}";
        }
        $startTag = "{$this->OpenTag}{$arrayElementName}{$this->CloseTag}";
        $xmlString .= $startTag;
        foreach($array as $key => $value){
            if(gettype($value) === "string" || gettype($value) === "boolean" || gettype($value) === "integer" || gettype($value) === "double" || gettype($value) === "float")
            {
                $elementStartTag = "{$this->OpenTag}{$arrayElementName}_{$key}{$this->CloseTag}";
                $elementEndTag = "{$this->OpenTag}{$this->BackSlash}{$arrayElementName}_{$key}{$this->CloseTag}";
                $xmlString .= "{$elementStartTag}{$value}{$elementEndTag}";
                continue;
            }
            else if(gettype($value) === "array")
            {
                $xmlString = $this->Array_To_XML($value, $arrayElementName, $xmlString);
                continue;
            }
            else if(gettype($value) === "object")
            {
                $xmlString = $this->Object_To_XML($value, $xmlString);
                continue;
            }
            else
            {                
                continue;
            }
        }
        $endTag = "{$this->OpenTag}{$this->BackSlash}{$arrayElementName}{$this->CloseTag}";
        $xmlString .= $endTag;
        return $xmlString;
    }

    private function Object_To_XML($objElement, $xmlString = "")
    {
        if($xmlString === "")
        {
            $xmlString = "{$this->OpenTag}{$this->Root}{$this->CloseTag}";
        }
        foreach($objElement as $key => $value){
            if(gettype($value) !== "array" && gettype($value) !== "object")
            {
                $startTag = "{$this->OpenTag}{$key}{$this->CloseTag}";
                $endTag = "{$this->OpenTag}{$this->BackSlash}{$key}{$this->CloseTag}";
                $xmlString .= "{$startTag}{$value}{$endTag}";
                continue;
            }
            else if(gettype($value) === "array")
            {
                $xmlString = $this->Array_To_XML($value, $key, $xmlString);
                continue;
            }
            else if(gettype($value) === "object")
            {
                $xmlString = $this->Object_To_XML($value, $xmlString);
                continue;
            }
            else
            { 
                continue;
            }
        }
        return $xmlString;
    }

    public function Serialize_Object($element, $xmlString = "")
    {
        $endTag = "{$this->OpenTag}{$this->BackSlash}{$this->Root}{$this->CloseTag}";
        return "{$this->Object_To_XML($element, $xmlString)}{$endTag}";
    }

    public function Serialize_Array($element, $xmlString = "")
    {
        $endTag = "{$this->OpenTag}{$this->BackSlash}{$this->Root}{$this->CloseTag}";
        return "{$this->Array_To_XML($element, $xmlString)}{$endTag}";
    }
}

Well, while a little dirty, you could always run a loop on the object's properties...

$_xml = '';
foreach($obj as $key => $val){
  $_xml .= '<' . $key . '>' . $val . '</' . $key . ">\n";
}

Using fopen/fwrite/fclose you could generate an XML doc with the $_xml variable as content. It's ugly, but it would work.

Related