working simplexmlelement simplexml_load_string simplexml_load_file recorrer not error ejemplos con array php xml xml-serialization

simplexmlelement - Objeto PHP como documento XML



string xml to array php (10)

Sé que esta es una vieja pregunta, pero recientemente tuve que generar estructuras XML complejas.

Mi enfoque contiene principios avanzados de POO. La idea es serializar el objeto principal que contiene varios hijos y sub hijos.

Los nodos obtienen nombres de los nombres de las clases, pero puede anular el nombre de la clase con el primer parámetro al crear un objeto para la serialización.

Puede crear: un nodo Simple, sin nodos secundarios, EntityList y ArrayList. EntityList es una lista de objetos de la misma clase, pero una ArrayList puede tener diferentes objetos.

Cada objeto tiene que extender la clase abstracta SerializeXmlAbstract para hacer coincidir el primer parámetro de entrada en la clase: Object2xml, method serialize($object, $name = NULL, $prefix = FALSE) .

De forma predeterminada, si no proporciona el segundo parámetro, el nodo raíz de XML tendrá el nombre de clase del objeto dado. El tercer parámetro indica si el nombre del nodo raíz tiene un prefijo o no. El prefijo está codificado como propiedad privada en la clase Export2xml.

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; } }

Este es el código que necesita para poder obtener xml válido de los objetos. La siguiente muestra produce este 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>

Las clases necesarias para este xml son:

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); } }

Y finalmente el código que instancia los objetos es:

$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);

Espero que ayude a alguien.

Saludos, Siniša

¿Cuál es la mejor manera de tomar un objeto PHP dado y serializarlo como XML? Estoy viendo simple_xml y lo he usado para analizar XML en objetos, pero no me queda claro cómo funciona al revés.


Aquí está mi código utilizado para serializar objetos de PHP a XML "comprensible" por 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; } }

ejemplo:

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);

resultados en:

<?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 una función dom para hacerlo: http://www.php.net/manual/en/function.dom-import-simplexml.php

Importe el objeto SimpleXML y luego guárdelo. El enlace de arriba contiene un ejemplo. :)

En una palabra:

<?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); } ?>


eche un vistazo al paquete XML_Serializer de PEAR. Lo he usado con muy buenos resultados. Puede alimentarlo con matrices, objetos, etc. y los convertirá en XML. También tiene un montón de opciones como elegir el nombre del nodo raíz, etc.

Debería hacer el truco



no es exactamente una respuesta a la pregunta original, pero la forma en que resolví mi problema con esto fue declarando mi objeto como:

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

Opuesto a:

$object = new stdClass;

antes de comenzar a agregar cualquier valor!


Estoy de acuerdo con el uso de XML_Serializer de PEAR, pero si quiere algo simple que admita objetos / matrices que tengan propiedades anidadas, puede usar esto.

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; } }


Aunque estoy de acuerdo con @philfreo y su razonamiento de que no deberías depender de PEAR, su solución aún no está allí. Hay problemas potenciales cuando la clave podría ser una cadena que contiene cualquiera de los siguientes caracteres:

  • <
  • >
  • / s (espacio)
  • "
  • ''

Cualquiera de estos arrojará el formato, ya que XML usa estos caracteres en su gramática. Entonces, sin más preámbulos, aquí hay una solución simple para esa posibilidad muy posible:

function xml_encode( $var, $indent = false, $i = 0 ) { $version = "1.0"; if ( !$i ) { $data = ''<?xml version="1.0"?>'' . ( $indent ? "/r/n" : '''' ) . ''<root vartype="'' . gettype( $var ) . ''" xml_encode_version="''. $version . ''">'' . ( $indent ? "/r/n" : '''' ); } else { $data = ''''; } foreach ( $var as $k => $v ) { $data .= ( $indent ? str_repeat( "/t", $i ) : '''' ) . ''<var vartype="'' .gettype( $v ) . ''" varname="'' . htmlentities( $k ) . ''"''; if($v == "") { $data .= '' />''; } else { $data .= ''>''; if ( is_array( $v ) ) { $data .= ( $indent ? "/r/n" : '''' ) . xml_encode( $v, $indent, $verbose, ($i + 1) ) . ( $indent ? str_repeat("/t", $i) : '''' ); } else if( is_object( $v ) ) { $data .= ( $indent ? "/r/n" : '''' ) . xml_encode( json_decode( json_encode( $v ), true ), $indent, $verbose, ($i + 1)) . ($indent ? str_repeat("/t", $i) : ''''); } else { $data .= htmlentities( $v ); } $data .= ''</var>''; } $data .= ($indent ? "/r/n" : ''''); } if ( !$i ) { $data .= ''</root>''; } return $data; }

Aquí hay un uso de muestra:

// sample object $tests = Array( "stringitem" => "stringvalue", "integeritem" => 1, "floatitem" => 1.00, "arrayitems" => Array("arrayvalue1", "arrayvalue2"), "hashitems" => Array( "hashkey1" => "hashkey1value", "hashkey2" => "hashkey2value" ), "literalnull" => null, "literalbool" => json_decode( json_encode( 1 ) ) ); // add an objectified version of itself as a child $tests[''objectitem''] = json_decode( json_encode( $tests ), false); // convert and output echo xml_encode( $tests ); /* // output: <?xml version="1.0"?> <root vartype="array" xml_encode_version="1.0"> <var vartype="integer" varname="integeritem">1</var> <var vartype="string" varname="stringitem">stringvalue</var> <var vartype="double" varname="floatitem">1</var> <var vartype="array" varname="arrayitems"> <var vartype="string" varname="0">arrayvalue1</var> <var vartype="string" varname="1">arrayvalue2</var> </var> <var vartype="array" varname="hashitems"> <var vartype="string" varname="hashkey1">hashkey1value</var> <var vartype="string" varname="hashkey2">hashkey2value</var> </var> <var vartype="NULL" varname="literalnull" /> <var vartype="integer" varname="literalbool">1</var> <var vartype="object" varname="objectitem"> <var vartype="string" varname="stringitem">stringvalue</var> <var vartype="integer" varname="integeritem">1</var> <var vartype="integer" varname="floatitem">1</var> <var vartype="array" varname="arrayitems"> <var vartype="string" varname="0">arrayvalue1</var> <var vartype="string" varname="1">arrayvalue2</var> </var> <var vartype="array" varname="hashitems"> <var vartype="string" varname="hashkey1">hashkey1value</var> <var vartype="string" varname="hashkey2">hashkey2value</var> </var> <var vartype="NULL" varname="literalnull" /> <var vartype="integer" varname="literalbool">1</var> </var> </root> */

Tenga en cuenta que los nombres de las claves se almacenan en el atributo varname (html encoded), e incluso el tipo se almacena, por lo que la deserialización simétrica es posible. Solo hay un problema con esto: no serializará clases, solo el objeto instanciado, que no incluirá los métodos de clase. Esto solo es funcional para pasar "datos" de ida y vuelta.

Espero que esto ayude a alguien, a pesar de que esto fue respondido hace mucho tiempo.


Bueno, aunque un poco sucio, siempre puedes ejecutar un bucle en las propiedades del objeto ...

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

Usando fopen / fwrite / fclose podrías generar un documento XML con la variable $_xml como contenido. Es feo, pero funcionaría.


echa un vistazo a mi versión

class XMLSerializer { /** * * The most advanced method of serialization. * * @param mixed $obj => can be an objectm, an array or string. may contain unlimited number of subobjects and subarrays * @param string $wrapper => main wrapper for the xml * @param array (key=>value) $replacements => an array with variable and object name replacements * @param boolean $add_header => whether to add header to the xml string * @param array (key=>value) $header_params => array with additional xml tag params * @param string $node_name => tag name in case of numeric array key */ public static function generateValidXmlFromMixiedObj($obj, $wrapper = null, $replacements=array(), $add_header = true, $header_params=array(), $node_name = ''node'') { $xml = ''''; if($add_header) $xml .= self::generateHeader($header_params); if($wrapper!=null) $xml .= ''<'' . $wrapper . ''>''; if(is_object($obj)) { $node_block = strtolower(get_class($obj)); if(isset($replacements[$node_block])) $node_block = $replacements[$node_block]; $xml .= ''<'' . $node_block . ''>''; $vars = get_object_vars($obj); if(!empty($vars)) { foreach($vars as $var_id => $var) { if(isset($replacements[$var_id])) $var_id = $replacements[$var_id]; $xml .= ''<'' . $var_id . ''>''; $xml .= self::generateValidXmlFromMixiedObj($var, null, $replacements, false, null, $node_name); $xml .= ''</'' . $var_id . ''>''; } } $xml .= ''</'' . $node_block . ''>''; } else if(is_array($obj)) { foreach($obj as $var_id => $var) { if(!is_object($var)) { if (is_numeric($var_id)) $var_id = $node_name; if(isset($replacements[$var_id])) $var_id = $replacements[$var_id]; $xml .= ''<'' . $var_id . ''>''; } $xml .= self::generateValidXmlFromMixiedObj($var, null, $replacements, false, null, $node_name); if(!is_object($var)) $xml .= ''</'' . $var_id . ''>''; } } else { $xml .= htmlspecialchars($obj, ENT_QUOTES); } if($wrapper!=null) $xml .= ''</'' . $wrapper . ''>''; return $xml; } /** * * xml header generator * @param array $params */ public static function generateHeader($params = array()) { $basic_params = array(''version'' => ''1.0'', ''encoding'' => ''UTF-8''); if(!empty($params)) $basic_params = array_merge($basic_params,$params); $header = ''<?xml''; foreach($basic_params as $k=>$v) { $header .= '' ''.$k.''=''.$v; } $header .= '' ?>''; return $header; } }