В SimpleXML, как я могу добавить существующий элемент SimpleXML в качестве дочернего элемента?
У меня есть объект SimpleXMLElement $дочерний и объект SimpleXMLElement $родительский.
Как я могу добавить $child в качестве дочернего элемента $parent? Есть ли какой-нибудь способ сделать это без преобразования в DOM и обратно?
Метод addChild(), похоже, позволяет мне создавать только новый пустой элемент, но это не помогает, когда у элемента, который я хочу добавить $child, также есть дочерние элементы. Я думаю, что здесь мне может понадобиться рекурсия.
4 answers
Я знаю, что это не самый полезный ответ, но тем более, что вы создаете/изменяете XML, я бы переключился на использование функций DOM. SimpleXML хорош для доступа к простым документам, но довольно плох в их изменении.
Если SimpleXML доброжелательно относится к вам во всех других местах, и вы хотите придерживаться его, у вас все еще есть возможность временно перейти к функциям DOM, чтобы выполнить то, что вам нужно, а затем снова вернуться, используя dom_import_simplexml()
и simplexml_import_dom()
. Я не уверен, насколько это эффективно, но это может вам помочь.
К сожалению 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 в виде фрагмента документа, это также описано в Как импортировать строку XML в PHP DOMDocument. Пример использования:
$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.
На самом деле, это возможно (динамически), если вы внимательно посмотрите, как определяется 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');
Оставляя это здесь, так как я только что наткнулся на эту страницу и обнаружил, что SimpleXML теперь поддерживает эту функциональность с помощью метода ::addChild.
Вы также можете использовать этот метод для добавления любых каскадных элементов:
$xml->addChild('parent');
$xml->parent->addChild('child');
$xml->parent->child->addChild('child_id','12345');