Pergunta

Qual é a melhor maneira de levar um dado objeto PHP e serializar-lo como XML? Estou olhando para simple_xml e eu tê-lo usado para analisar XML em objetos, mas não está claro para mim como ele funciona ao contrário.

Foi útil?

Solução

vejam o pacote XML_Serializer do PEAR. Eu usei-o com bons resultados bonitas. Você pode alimentá-lo matrizes, objetos etc e vai transformá-los em XML. Ele também tem um monte de opções, como escolher o nome da raiz nó etc.

Deve fazer o truque

Outras dicas

Eu concordo com o uso XML_Serializer da PERA, mas se você quer algo simples que suporta objetos / matrizes que têm propriedades aninhadas, você pode usar isso.

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

}

não é bem uma resposta para a pergunta original, mas a maneira que eu resolvi meu problema com este foi declarando meu objeto como:

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

ao contrário:

$object = new stdClass;

Antes de começar a adição de quaisquer valores!

Use uma função dom de fazê-lo: http://www.php.net/manual/en/function .dom-import-simplexml.php

Importar o objeto SimpleXML e depois salvar. A ligação acima contém um exemplo. :)

Em poucas palavras:

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

dar uma olhada em minha versão

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

usar WDDX: http://uk.php.net/manual/en/wddx.examples. php

(se esta extensão está instalado)

é dedicada a isso:

http://www.openwddx.org/

Eu sei que isto é uma questão antiga, mas recentemente tive a gerar estruturas XML complexos.

A minha abordagem contém princípios OOP avançados. A ideia é serializar o objeto pai que contém várias crianças e subchildren.

Nós obter os nomes dos nomes de classe, mas você pode substituir o nome da classe com o primeiro parâmetro ao criar um objeto para serialização.

Você pode criar: um nó simples, sem nós filhos, EntityList e ArrayList. EntityList é uma lista de objetos da mesma classe, mas um ArrayList pode ter objetos diferentes.

Cada objeto tem de estender a classe abstrata SerializeXmlAbstract , a fim de corresponder a parâmetro primeira entrada na sala de aula:. Object2xml, método serialize($object, $name = NULL, $prefix = FALSE)

Por padrão, se você não fornecer o segundo parâmetro, o nó XML raiz terá o nome da classe do objeto dado. O terceiro parâmetro indica se o nome do nó raiz tem um prefixo ou não. Prefixo é codificado como uma propriedade privada na classe 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 é o código que você precisa para ser capaz de obter um XML válido a partir de objetos. Próxima amostra produz 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>

Classes necessários para este xml são:

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

E, finalmente código que instanciar objetos é:

$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 ele vai ajudar alguém.

Cheers, Siniša

Aqui está o meu código usado para a serialização PHP objetos para XML "compreensível" pelo 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;
    }
}

exemplo:

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

resulta em:

<?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 método recursivo, como este:

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

Aqui, o exemplo completo: http://www.tyrodeveloper.com/ 2018/09 / convertir-clase-en-xml-con-php.html

Embora concorde com @philfreo e seu raciocínio que você não deve ser dependente PERA, sua solução ainda não está completamente lá. Há problemas potenciais quando a chave poderia ser uma cadeia que contém qualquer um dos seguintes caracteres:

  • <
  • >
  • \ s (espaço)
  • "
  • '

Qualquer uma dessas vai jogar fora o formato, como XML usa esses caracteres em sua gramática. Assim, sem mais delongas, aqui é uma solução simples para isso muito possível ocorrência:

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

Aqui é um uso de amostra:

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

*/

Observe que os nomes das teclas são armazenados no atributo varname (html codificado), e até mesmo o tipo é armazenado, de forma simétrica de-serialização é possível. Há apenas um problema com isso: ele vai classes não serialize, apenas o objeto instanciado, o que não irá incluir os métodos de classe. Esta é apenas funcional para a passagem de "dados" e para trás.

Espero que isso ajude alguém, mesmo que este foi respondida há muito tempo.

Bem, enquanto um pouco sujo, você pode sempre executar um loop sobre as propriedades do objeto ...

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

Usando fopen / fwrite / fclose você poderia gerar um documento XML com a variável $_xml como conteúdo. É feio, mas ele iria trabalhar.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top