9

我有一个 SimpleXMLElement 对象 $child 和一个 SimpleXMLElement 对象 $parent。

如何将 $child 添加为 $parent 的子级?有没有办法做到这一点而无需转换为 DOM 并返回?

addChild() 方法似乎只允许我创建一个新的空元素,但是当我要添加的元素 $child 也有子元素时,这无济于事。我想我可能需要在这里递归。

4

4 回答 4

10

不幸的是SimpleXMLElement,它没有提供任何东西来将两个元素结合在一起。正如@nickf 所写,它更适合阅读而不是操作。但是,姊妹扩展名DOMDocument用于编辑,您可以通过dom_import_simplexml(). @salathe在相关答案中显示了这对于特定的 SimpleXMLElements 是如何工作的。

下面显示了它如何与输入检查和更多选项一起工作。我用两个例子来做。第一个示例是插入 XML 字符串的函数:

/**
 * Insert XML into a SimpleXMLElement
 *
 * @param SimpleXMLElement $parent
 * @param string $xml
 * @param bool $before
 * @return bool XML string added
 */
function simplexml_import_xml(SimpleXMLElement $parent, $xml, $before = false)
{
    $xml = (string)$xml;

    // check if there is something to add
    if ($nodata = !strlen($xml) or $parent[0] == NULL) {
        return $nodata;
    }

    // add the XML
    $node     = dom_import_simplexml($parent);
    $fragment = $node->ownerDocument->createDocumentFragment();
    $fragment->appendXML($xml);

    if ($before) {
        return (bool)$node->parentNode->insertBefore($fragment, $node);
    }

    return (bool)$node->appendChild($fragment);
}

该示例性功能允许附加 XML 或将其插入到某个元素之前,包括根元素。在确定是否有要添加的内容后,它利用DOMDocument 函数和方法将 XML 作为文档片段插入,这也在如何在 PHP DOMDocument 中导入 XML 字符串中进行了概述。使用示例:

$parent = new SimpleXMLElement('<parent/>');

// insert some XML
simplexml_import_xml($parent, "\n  <test><this>now</this></test>\n");

// insert some XML before a certain element, here the first <test> element
// that was just added
simplexml_import_xml($parent->test, "<!-- leave a comment -->\n  ", $before = true);

// you can place comments above the root element
simplexml_import_xml($parent, "<!-- this works, too -->", $before = true);

// but take care, you can produce invalid XML, too:
// simplexml_add_xml($parent, "<warn><but>take care!</but> you can produce invalid XML, too</warn>", $before = true);

echo $parent->asXML();

这给出了以下输出:

<?xml version="1.0"?>
<!-- this works, too -->
<parent>
  <!-- leave a comment -->
  <test><this>now</this></test>
</parent>

第二个例子是插入一个SimpleXMLElement. 如果需要,它会使用第一个功能。它基本上检查是否有事情要做以及要导入哪种元素。如果是属性,则直接添加,如果是元素,则序列化为XML,然后作为XML添加到父元素中:

/**
 * Insert SimpleXMLElement into SimpleXMLElement
 *
 * @param SimpleXMLElement $parent
 * @param SimpleXMLElement $child
 * @param bool $before
 * @return bool SimpleXMLElement added
 */
function simplexml_import_simplexml(SimpleXMLElement $parent, SimpleXMLElement $child, $before = false)
{
    // check if there is something to add
    if ($child[0] == NULL) {
        return true;
    }

    // if it is a list of SimpleXMLElements default to the first one
    $child = $child[0];

    // insert attribute
    if ($child->xpath('.') != array($child)) {
        $parent[$child->getName()] = (string)$child;
        return true;
    }

    $xml = $child->asXML();

    // remove the XML declaration on document elements
    if ($child->xpath('/*') == array($child)) {
        $pos = strpos($xml, "\n");
        $xml = substr($xml, $pos + 1);
    }

    return simplexml_import_xml($parent, $xml, $before);
}

这个示例性函数确实标准化了元素列表和属性列表,就像 Simplexml 中常见的一样。您可能希望将其更改为一次插入多个 SimpleXMLElements,但正如下面的用法示例所示,我的示例不支持该示例(请参阅属性示例):

// append the element itself to itself
simplexml_import_simplexml($parent, $parent);

// insert <this> before the first child element (<test>)
simplexml_import_simplexml($parent->children(), $parent->test->this, true);

// add an attribute to the document element
$test = new SimpleXMLElement('<test attribute="value" />');
simplexml_import_simplexml($parent, $test->attributes());

echo $parent->asXML();

这是第一个使用示例的延续。因此现在的输出是:

<?xml version="1.0"?>
<!-- this works, too -->
<parent attribute="value">
  <!-- leave a comment -->
  <this>now</this><test><this>now</this></test>
<!-- this works, too -->
<parent>
  <!-- leave a comment -->
  <test><this>now</this></test>
</parent>
</parent>

我希望这是有帮助的。您可以在 gist在线演示/ PHP 版本概述中找到代码。

于 2013-02-12T11:24:23.190 回答
6

我知道这不是最有用的答案,但特别是因为您正在创建/修改 XML,我会切换到使用 DOM 函数。SimpleXML 非常适合访问简单的文档,但在更改它们方面却很差。

如果 SimpleXML 在所有其他地方都对您很好,并且您想坚持下去,您仍然可以选择暂时跳到 DOM 函数以执行您需要的操作,然后再跳回来,使用dom_import_simplexml()and simplexml_import_dom()。我不确定这有多有效,但它可能会帮助你。

于 2009-04-20T08:16:49.040 回答
-2

实际上,如果您仔细查看addChild()定义的方式,则可能(动态地)。我使用这种技术通过递归和引用传递将任何数组转换为 XML

  • addChild()添加的孩子的回报SimpleXMLElement
  • 要添加叶节点,请使用$xml->addChilde($nodeName, $nodeValue).
  • 要添加可能具有子节点或值的节点,请使用 $xml->addChilde($nodeName),没有值传递给addChild()。这将产生一个 SimpleXMLElement 类型的子节点!不是字符串!

目标 XML

<root>
    <node>xyz</node>
    <node>
        <node>aaa</node>
        <node>bbb</node>
    </node>
</root>

代码:

$root = new SimpleXMLElement('<root />');
//add child with name and string value.
$root.addChild('node', 'xyz'); 
//adds child with name as root of new SimpleXMLElement
$sub = $root->addChild('node');
$sub.addChild('node', 'aaa');
$sub.addChild('node', 'bbb');
于 2013-01-16T16:32:11.903 回答
-3

把这个留在这里,因为我偶然发现了这个页面,发现 SimpleXML 现在通过::addChild 方法支持这个功能。

您也可以使用此方法添加任何级联元素:

$xml->addChild('parent');
$xml->parent->addChild('child');
$xml->parent->child->addChild('child_id','12345');
于 2012-06-11T23:15:45.177 回答