PHP对象作为XML文档

50

如何将给定的PHP对象序列化为XML?我在查看simple_xml,我已经使用它将XML解析为对象,但是我不清楚如何实现相反的过程。

12个回答

54

我同意使用 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;
    }

}

3
很好,对我来说非常有效。特别是如果您不想从PEAR获取任何依赖关系。 - dermatthias
有人能给个使用这个的例子吗?我真的很困惑。 - Matt Boyle
2
@MattBoyle,我使用json_decode将simpleXML转换为对象。尝试使用以下代码处理你的对象。$xml_generater = new XMLSerializer; $std_class = json_decode(json_encode($object)); $xml = $xml_generater->generateValidXmlFromObj($std_class); - Goose
1
非常有用,谢谢。但是如果您需要生成具有相同深度中的多个项目的XML,则将不足够,因为数组键不能相同。 - dwenaus
@Philfreo,感谢您的解决方案,在Laravel 5.5中它给了我一个错误对象不是stdClass实例。直到我从generateValidXmlFromObj方法中删除stdClass,然后它就正常工作了。 - Yaqoob Al-Shuaibi

39

看看PEAR的XML_Serializer 软件包。我用它效果还不错。你可以将数组、对象等放入其中,它会把它们转换成XML格式。它还有很多选项,比如选择根节点的名称等。

应该能解决问题。


3
我今天想在PHP 5.3.13上使用这个程序包(在此回答4年后),但是XML_Serializer和它的依赖项出现了“Strict Standards”错误。您知道其他替代方案吗? - djule5
4
警告,这个库有非常糟糕的依赖关系! - clankill3r

10

虽然不完全回答了原问题,但我解决这个问题的方法是将我的对象声明为:

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

相对于:

$object = new stdClass;

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


当您完成后,只需调用asXML()函数以获取生成的XML,或者对于内部xml,请使用children()->asXml()。太棒了! - yoel halb

7
使用dom函数来完成: http://www.php.net/manual/zh/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);
}
?>

1
我认为这并没有回答问题,我很确定他想做的是将array("foo" => "bar")转换成"<xml><foo>bar</foo></xml>"(不完全一样,但你明白我的意思)。 - davr
你可以填充一个simplexml对象吗? - Till
但他并没有询问如何将simplexml对象转换为XML,而是想要普通的XML对象。除非我误解了,否则我没有看到一种简单的方法可以将任意PHP数据结构(对象、数组、字符串等)转换为simplexml对象(从而转换为XML字符串)。 - davr
仍然没有回答问题。您正在提供一个数组,而不是一个对象。即使您首先使用了get_object_vars,它也无法递归地工作。 - philfreo
仅进行一级转换 - vladkras

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

1
你能将这与之前提供的选项进行比较和对比吗? - trex005
非常好的解决方案!!!它可以很好地处理深度对象和数组。谢谢。 - Silvio Delgado
不错的解决方案。小备注…… 在定义了多层命名空间的类的情况下,您可能会陷入麻烦。在使用带重音的“属性/值”时,您也可能会遇到麻烦。 - user2816761
当值被转换为标签名称时,如果这些值包含无效字符,则也必须考虑到它们。 - user2816761

2

1
"www.openwddx.org 可出售。" - Michel

1

这是我的代码,用于将PHP对象序列化为Microsoft .NET XmlSerializer.Deserialize“可理解”的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


请注意在不同网站上链接到自己的内容,您不想成为垃圾邮件发送者。您应该在此处包含大部分内容,并仅将链接用作参考。 - Dharman

0

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

我的方法包含高级面向对象编程原则。思路是序列化包含多个子节点和子子节点的父对象。

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

您可以创建:简单节点(没有子节点)、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);

希望能对某些人有所帮助。

祝好, Siniša


0

最近我创建了一个可以通过Git获取的类来解决这个问题:

https://github.com/zappz88/XMLSerializer

这里是类的结构,请记住您需要适当定义根以格式化正确的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}";
    }
}

...而且这种格式可以被PHP的xmlsimpleloadobject函数识别和解析。 - user10555044

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接