PHP对象作为XML文档

问题描述 投票:47回答:11

获取给定PHP对象并将其序列化为XML的最佳方法是什么?我正在看simple_xml,我用它来将XML解析为对象,但我不清楚它是如何工作的。

php xml xml-serialization
11个回答
39
投票

看看PEAR的XML_Serializer套餐。我使用它的效果非常好。您可以为它提供数组,对象等,并将它们转换为XML。它还有一些选项,例如选择根节点的名称等。

应该做的伎俩


-1
投票

虽然我同意@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编码)中,甚至存储了类型,因此可以进行对称反序列化。这只有一个问题:它不会序列化类,只会序列化实例化的对象,它不包括类方法。这仅适用于来回传递“数据”。

我希望这有助于某人,即使很久以前就已经回答了这个问题。


-1
投票

好吧,虽然有点脏,你总是可以在对象的属性上运行循环...

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

使用fopen / fwrite / fclose,您可以使用$_xml变量作为内容生成XML文档。它很丑,但它会起作用。


49
投票

我同意使用PEAR的XML_Serializer,但如果你想要一些简单的东西支持具有嵌套属性的对象/数组,你可以使用它。

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

}

9
投票

对原始问题的回答不是很完美,但我解决问题的方法是将对象声明为:

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

而不是:

$object = new stdClass;

在我开始添加任何值之前!


5
投票

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

3
投票

看看我的版本

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

2
投票

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

(如果安装了此扩展程序)

它致力于:

http://www.openwddx.org/


0
投票

我知道这是一个老问题,但最近我不得不生成复杂的XML结构。

我的方法包含先进的OOP原则。我们的想法是序列化包含多个子项和子项的父对象。

节点从类名中获取名称,但在创建序列化对象时,可以使用第一个参数覆盖类名。

您可以创建:一个简单节点,没有子节点,EntityList和ArrayList。 EntityList是同一类的对象列表,但ArrayList可能具有不同的对象。

每个对象都必须扩展抽象类SerializeXmlAbstract,以匹配类中的第一个输入参数:Object2xml,方法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);

希望它会帮助某人。

干杯,西尼沙


0
投票

这是我的代码,用于通过Microsoft .NET XmlSerializer.Deserialize将PHP对象序列化为XML“可理解”

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>

0
投票

使用递归方法,如下所示:

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

这里有完整的例子:http://www.tyrodeveloper.com/2018/09/convertir-clase-en-xml-con-php.html

© www.soinside.com 2019 - 2024. All rights reserved.