PHP-объект как XML-документ

Каков наилучший способ взять данный объект PHP и сериализовать его как XML? Я смотрю на simple_xml, и я использовал его для анализа XML в объектах, но мне непонятно, как это работает наоборот.

взгляните на пакет XML_Serializer PEAR. Я использовал его с хорошими результатами. Вы можете кормить его массивами, объектами и т. Д., И это превратит их в XML. Он также имеет множество параметров, таких как выбор имени корневого узла и т. Д.

Должен сделать трюк

Я бы согласился с использованием XML_Serializer PEAR, но если вы хотите что-то простое, поддерживающее объекты / массивы с вложенными свойствами, вы можете использовать это.

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

не совсем ответ на исходный вопрос, но так, как я решил свою проблему с этим, объявлял мой объект как:

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

в отличие от:

 $object = new stdClass; 

прежде чем я начал добавлять какие-либо значения!

Для выполнения этой функции используйте функцию dom: http://www.php.net/manual/en/function.dom-import-simplexml.php

Импортируйте объект SimpleXML, а затем сохраните. В приведенной выше ссылке приведен пример. 🙂

В двух словах:

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

используйте WDDX: http://uk.php.net/manual/en/wddx.examples.php

(если это расширение установлено)

он посвящен этому:

http://www.openwddx.org/

взгляните на мою версию

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

Я знаю, что это старый вопрос, но в последнее время мне пришлось создавать сложные структуры XML.

Мой подход содержит передовые принципы ООП. Идея состоит в сериализации родительского объекта, который содержит несколько дочерних и дочерних элементов.

Узлы получают имена от имен классов, но вы можете переопределить имя класса с помощью первого параметра при создании объекта для сериализации.

Вы можете создать: простой узел, без дочерних узлов, EntityList и ArrayList. EntityList – это список объектов одного класса, но ArrayList может иметь разные объекты.

Каждый объект должен расширять абстрактный класс SerializeXmlAbstract , чтобы соответствовать первому входному параметру в классе: Object2xml, serialize($object, $name = NULL, $prefix = FALSE) метода serialize($object, $name = NULL, $prefix = FALSE) .

По умолчанию, если вы не предоставляете второй параметр, корневой узел XML будет иметь имя класса для данного объекта. Третий параметр указывает, имеет ли имя корневого узла префикс или нет. Префикс жестко закодирован как частное свойство в классе 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; } } 

Это код, который вам нужен, чтобы получить действительный xml из объектов. Следующий образец создает этот 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> 

Классы, необходимые для этого xml:

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

И, наконец, код, создающий объекты:

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

Надеюсь, это поможет кому-то.

Приветствия, Синиша

Вот мой код, используемый для сериализации объектов PHP для XML «понятным» с помощью 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; } } 

пример:

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

результаты:

 <?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> 

Хотя я согласен с @philfreo и его рассуждениями о том, что вы не должны зависеть от PEAR, его решение все еще не совсем там. Существуют потенциальные проблемы, когда ключ может быть строкой, содержащей любой из следующих символов:

  • <
  • >
  • \ s (пробел)
  • "
  • '

Любой из них отбросит формат, поскольку XML использует эти символы в своей грамматике. Таким образом, без дальнейших церемоний, вот простое решение этого очень возможного события:

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

Вот пример использования:

 // 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> */ 

Обратите внимание, что имена ключей хранятся в атрибуте varname (html encoded), и даже тип сохраняется, поэтому возможна симметричная де-сериализация. Существует только одна проблема: он не будет сериализовать классы, а только экземпляр объекта, который не будет включать методы класса. Это работает только для передачи «данных» взад и вперед.

Надеюсь, это поможет кому-то, хотя это уже давно ответили.

Ну, хотя немного грязно, вы всегда можете запустить цикл свойств объекта …

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

Используя fopen / fwrite / fclose, вы можете создать XML-документ с переменной $_xml качестве содержимого. Это уродливо, но это сработает.