Как в SimpleXML добавить существующий элемент SimpleXMLElement в качестве дочернего элемента? - PullRequest
6 голосов
/ 20 апреля 2009

У меня есть объект SimpleXMLElement $ child и объект SimpleXMLElement $ parent.

Как я могу добавить $ child как потомок $ parent? Есть ли способ сделать это без преобразования в DOM и обратно?

Метод addChild () только позволяет мне создать новый пустой элемент, но это не помогает, когда элемент, который я хочу добавить, $ child также имеет дочерние элементы. Я думаю, мне здесь может понадобиться рекурсия.

Ответы [ 4 ]

7 голосов
/ 12 февраля 2013

К сожалению 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. Возможно, вы захотите изменить его, чтобы вставить несколько простых элементов SimpleXMLE одновременно, но, как показано в примере использования ниже, мой пример не поддерживает это (см. Пример атрибутов):

// 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>

Надеюсь, это полезно. Вы можете найти код в гисте и как онлайн-демонстрацию / Обзор версии PHP .

6 голосов
/ 20 апреля 2009

Я знаю, что это не самый полезный ответ, но, поскольку вы создаете / модифицируете XML, я бы переключился на использование функций DOM. SimpleXML удобен для доступа к простым документам, но довольно слаб при их изменении.

Если SimpleXML относится к вам любезно во всех других местах, и вы хотите придерживаться его, у вас все еще есть возможность временно переключиться на функции DOM, чтобы выполнить то, что вам нужно, а затем снова вернуться, используя dom_import_simplexml() и simplexml_import_dom(). Я не уверен, насколько это эффективно, но это может вам помочь.

0 голосов
/ 16 января 2013

На самом деле, это возможно (динамически), если вы внимательно посмотрите, как определяется 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');
0 голосов
/ 12 июня 2012

Оставив это здесь, я только что наткнулся на эту страницу и обнаружил, что SimpleXML теперь поддерживает эту функцию с помощью метода :: addChild .

Вы также можете использовать этот метод для добавления любых каскадных элементов:

$xml->addChild('parent');
$xml->parent->addChild('child');
$xml->parent->child->addChild('child_id','12345');
...