PHP - SimpleXML - AddChild 与另一个 SimpleXMLElement

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4778865/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-25 14:20:27  来源:igfitidea点击:

PHP - SimpleXML - AddChild with another SimpleXMLElement

phpsimplexml

提问by Ben

I'm trying to build a rather complex XML document.

我正在尝试构建一个相当复杂的 XML 文档。

I have a bunch of sections of the XML document that repeats. I thought I'd use multiple string templates as base document for the sections and create instances of XML elements using simplexml_load_string.

我有一堆重复的 XML 文档部分。我想我会使用多个字符串模板作为部分的基本文档,并使用 simplexml_load_string 创建 XML 元素的实例。

So I have one instance of SimpleXMLElement as the base document

所以我有一个 SimpleXMLElement 实例作为基础文档

$root = simplexml_load_string($template_root);

$root = simplexml_load_string($template_root);

then I loop through some items in my database, create new SimpleXMLElement, something like this:

然后我遍历数据库中的一些项目,创建新的 SimpleXMLElement,如下所示:

for (bla bla bla):

$item = simplexml_load_string($template_item); // do stuff with item // try to add item to the root document..
// Stuck here.. can't do $root->items->addChild($item)

endfor;

对于(bla bla bla):

$item = simplexml_load_string($template_item); // 用 item 做东西 // 尝试将 item 添加到根文档中..
// 卡在这里.. can't do $root->items->addChild($item)

结束;

I can't call addChild because it just expects a tag name and value.. you can't addChild another SimpleXMLElement.

我不能调用 addChild 因为它只需要一个标签名称和值..你不能 addChild 另一个 SimpleXMLElement。

Am I missing something here? seems really dumb that addChild can't take a SimpleXMLELement as a parameter.

我在这里错过了什么吗?addChild 不能将 SimpleXMLELement 作为参数似乎真的很愚蠢。

Is there any other way to do this? (apart from using a different xml lib)

有没有其他方法可以做到这一点?(除了使用不同的 xml 库)

回答by Artefacto

As far as I know, you can't do it with SimpleXML because addChilddoesn't make a deep copy of the element (being necessary to specify the tag name can easily be overcome by calling SimpleXMLElement::getName()).

据我所知,你不能用 SimpleXML 来做到这一点,因为addChild它没有制作元素的深层副本(需要指定标签名称可以很容易地通过调用来克服SimpleXMLElement::getName())。

One solution would be to use DOM instead:

一种解决方案是改用 DOM:

With this function:

有了这个功能:

function sxml_append(SimpleXMLElement $to, SimpleXMLElement $from) {
    $toDom = dom_import_simplexml($to);
    $fromDom = dom_import_simplexml($from);
    $toDom->appendChild($toDom->ownerDocument->importNode($fromDom, true));
}

We have for

我们有

<?php
header("Content-type: text/plain");
$sxml = simplexml_load_string("<root></root>");

$n1 = simplexml_load_string("<child>one</child>");
$n2 = simplexml_load_string("<child><k>two</k></child>");

sxml_append($sxml, $n1);
sxml_append($sxml, $n2);

echo $sxml->asXML();

the output

输出

<?xml version="1.0"?>
<root><child>one</child><child><k>two</k></child></root>

See also some user comments that use recursive functions and addChild, e.g. this one.

另请参阅一些使用递归函数的用户评论addChild,例如这个

回答by Carlos C Soto

You could use this function that is based in creating the children with attributes from the source:

您可以使用基于从源创建具有属性的子项的此函数:

function xml_adopt($root, $new) {
    $node = $root->addChild($new->getName(), (string) $new);
    foreach($new->attributes() as $attr => $value) {
        $node->addAttribute($attr, $value);
    }
    foreach($new->children() as $ch) {
        xml_adopt($node, $ch);
    }
}

$xml = new SimpleXMLElement("<root/>");
$child = new SimpleXMLElement("<content><p a=\"aaaaaaa\">a paragraph</p><p>another <br/>p</p></content>");

xml_adopt($xml, $child);
echo $xml->asXML()."\n";

This will produce:

这将产生:

<?xml version="1.0"?>
<root><content><p a="aaaaaaa">a paragraph</p><p>another p<br/></p></content></root>

回答by GDmac

The xml_adopt() example doesn't preserve namespace nodes.
My edit was rejected because it changed to much? was spam?.

xml_adopt() 示例不保留名称空间节点。
我的编辑被拒绝是因为它改变了很多?是垃圾邮件?。

Here is a version of xml_adopt() that preserves namespaces.

这是保留名称空间的 xml_adopt() 版本。

function xml_adopt($root, $new, $namespace = null) {
    // first add the new node
    // NOTE: addChild does NOT escape "&" ampersands in (string)$new !!!
    //  replace them or use htmlspecialchars(). see addchild docs comments.
    $node = $root->addChild($new->getName(), (string) $new, $namespace);
    // add any attributes for the new node
    foreach($new->attributes() as $attr => $value) {
        $node->addAttribute($attr, $value);
    }
    // get all namespaces, include a blank one
    $namespaces = array_merge(array(null), $new->getNameSpaces(true));
    // add any child nodes, including optional namespace
    foreach($namespaces as $space) {
      foreach ($new->children($space) as $child) {
        xml_adopt($node, $child, $space);
      }
    }
}

(edit: example added)

(编辑:添加示例)

$xml = new SimpleXMLElement(
  '<?xml version="1.0" encoding="utf-8"?>
  <rss version="2.0" xmlns:media="http://search.yahoo.com/mrss/">
  <channel></channel></rss>');

$item = new SimpleXMLElement(
  '<item xmlns:media="http://search.yahoo.com/mrss/">
    <title>Slide Title</title>
    <description>Some description</description>
    <link>http://example.com/img/image.jpg</link>
    <guid isPermaLink="false">A1234</guid>
    <media:content url="http://example.com/img/image.jpg" medium="image" duration="15">
    </media:content>
  </item>');

$channel = $xml->channel;
xml_adopt($channel, $item);

// output:
// Note that the namespace is (correctly) only preserved on the root element
'<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
  <channel>
    <item>
      <title>Slide Title</title>
      <description>Some description</description>
      <link>http://example.com/img/image.jpg</link>
      <guid isPermaLink="false">A1234</guid>
      <media:content url="http://example.com/img/image.jpg" medium="image" duration="15">
        </media:content>
    </item>
  </channel>
</rss>'