SimpleXML — добавить новый узел, используя ранее объявленное пространство имен — как?

Я хотел бы добавить дочерний элемент в очень конкретном месте (поэтому я также использую DOM, а не только simpleXML) для узла <domain:create>.

Я попытался использовать атрибут $ns в конструкции simpleXML.

$nsNode = new SimpleXMLElement('<domain:ns>', $options = 0, $ns='urn:ietf:params:xml:ns:domain-1.0');

//transform the target into dom object for manipulation
$nodeRegistrantDom = dom_import_simplexml($nodeRegistrant);

Но я получаю:

Предупреждение ввода-вывода: не удалось загрузить внешний объект "<domain:ns>"

Я пытался зарегистрировать префикс после создания элемента, но после этого я не использую xpath, так что это была совершенно бесполезная попытка...

//creates the simpleXML object node to be inserted.
$nsNode = new SimpleXMLElement('<ns/>');

//this will not work, because we will not use xpath after it :s
$nsNode->registerXPathNamespace('domain', 'urn:ietf:params:xml:ns:domain-1.0');

Поскольку xml загружается из файла, и этот файл объявлен как this ns, может быть, нам следует взять его из этого файла?

Вот общий итог вышеизложенного, чтобы мы могли лучше понять контекст: Мы загружаем файл XML, который содержит общую структуру:

 $xmlObj = simplexml_load_file('EppCreateDomain.xml');

В них мы возьмем элемент, который будем использовать в качестве цели:

//grab the target.
    $nodeRegistrant = $xmlObj->command->create->children(self::OBJ_URI_DOMAIN)->create->registrant;

    //transform the target into a dom object for later manipulation
    $nodeRegistrantDom = dom_import_simplexml($nodeRegistrant);

//we try to use simpleXML to create the node that we want to add after our target.
    $nsNode = new SimpleXMLElement('<domain:ns>');


//grabs the node and all his children (none in this case), by importing the node we want to add,
//into the root object element that contains the <domain:registrant> node.
$nsNodeDom = $nodeRegistrantDom->ownerDocument->importNode(dom_import_simplexml($nsNode), true);

$nodeRegistrantDom->parentNode->insertBefore($nsNodeDom, $nodeRegistrantDom->nextSibling);

$simpleXmlNsNode = simplexml_import_dom($nsNodeDom);

Теперь у нас есть наш узел, размещенный в нужном месте. И преобразовали в simpleXML, поэтому теперь мы можем легко добавить несколько дочерних элементов и заполнить остальную часть файла xml.

$hostAttr = $simpleXmlNsNode->addChild('domain:hostAttr');
$hostName = $hostAttr->addChild('domain:hostName');

Посоветуйте, МЭМ


person MEM    schedule 09.08.2010    source источник
comment
Можете ли вы опубликовать небольшой автономный пример, который показывает вашу проблему? По крайней мере, я немного смущен тем, что происходит, где и почему. Например. $nodeRegistrant? Откуда это?   -  person VolkerK    schedule 09.08.2010
comment
Конечно. Немедленно. Было довольно сложно вырезать часть его, чтобы поставить вопрос. Я добавлю еще немного информации. 5 минут.   -  person MEM    schedule 09.08.2010


Ответы (2)


<?php
// test document, registrant as first/last element and somewhere in between
$xmlObj = new SimpleXMLElement('<epp>
  <domain:create xmlns:domain="urn:someurn">
    <domain:name></domain:name>
    <domain:registrant></domain:registrant>
    <domain:contact></domain:contact>
  </domain:create>
  <domain:create xmlns:domain="urn:someurn">
    <domain:name></domain:name>
    <domain:contact></domain:contact>
    <domain:registrant></domain:registrant>
  </domain:create>
  <domain:create xmlns:domain="urn:someurn">
    <domain:registrant></domain:registrant>
    <domain:name></domain:name>
    <domain:contact></domain:contact>
  </domain:create>
</epp>');

foreach( $xmlObj->children("urn:someurn")->create as $create ) {
  $registrant = $create->registrant;
  insertAfter($registrant, 'domain:ns', 'some text');
}
echo $xmlObj->asXML();

function insertAfter(SimpleXMLElement $prevSibling, $qname, $val) {
  $sd = dom_import_simplexml($prevSibling);
  $newNode = $sd->ownerDocument->createElement($qname, $val);
  $newNode = $sd->parentNode->insertBefore($newNode, $sd->nextSibling);
  return simplexml_import_dom($newNode);
}

отпечатки

<?xml version="1.0"?>
<epp>
  <domain:create xmlns:domain="urn:someurn">
    <domain:name/>
    <domain:registrant/><domain:ns>some text</domain:ns>
    <domain:contact/>
  </domain:create>
  <domain:create xmlns:domain="urn:someurn">
    <domain:name/>
    <domain:contact/>
    <domain:registrant/><domain:ns>some text</domain:ns>
  </domain:create>
  <domain:create xmlns:domain="urn:someurn">
    <domain:registrant/><domain:ns>some text</domain:ns>
    <domain:name/>
    <domain:contact/>
  </domain:create>
</epp>
person VolkerK    schedule 09.08.2010
comment
Я изучаю... :) Мне понадобится пара часов. Да я тот манекен. - person MEM; 09.08.2010
comment
Здравствуйте еще раз, не могли бы вы, ребята, ответить на этот вопрос: 1) Оба ваших ответа были в форме функции. Почему? Да, это очень простой вопрос, но я также очень простой кодер, так как у меня не более 1 года опыта в этом. :s Еще раз спасибо, МЭМ - person MEM; 09.08.2010
comment
Сомнения 2: Почему createElement('domain:ns'); работает, и этот importNode(dom_import_simplexml($nsNode), true); нет? - person MEM; 09.08.2010
comment
Сомнение 3: Когда мы используем createElement, это означает, что он будет создан, но размещен где угодно? И это из-за того, ЧТО мы можем использовать insertBefore, чтобы правильно разместить его? - person MEM; 09.08.2010
comment
Сомнение 4: если нам нужно добавить некоторые дочерние элементы к нашему элементу, и этим дочерним элементам также нужен префикс пространства имен, мы должны: $hostAttr = $simpleXmlNsNode->addChild('domain:hostAttr', null, self::OBJ_URI_DOMAIN) ; Почему нам нужно повторно объявить наше пространство имен. Не должно быть там, когда мы возвращаем simplexml_import_dom($newNode); ? - person MEM; 09.08.2010
comment
Boolean отвечает на мой достаточный. Извините, код работает, он идеален, мне он нравится, но я также хотел бы правильно его понять. :) Спасибо. - person MEM; 09.08.2010
comment
много вопросов ;-) 1) Легче в использовании, легче копировать, проще демонстрационный код. 2) Вы создали новый отдельный документ с помощью new SimpleXMLElement(...), в то время как createElement работает в том же документе, где уже зарегистрирован ns. 3) сам createElement() размещает элемент в любом месте документа, вы должны сделать это самостоятельно, например. через appendChild(),insertBefeore()... 4) ??? - person VolkerK; 09.08.2010
comment
1) ок. 3) Я прочитал часть руководства по php для createElement, понятно. :) 4) Нп. Это заслуживает вопроса само по себе. Теперь: 2) Понятно... но мы импортировали в документ владельца, или, по крайней мере, это было моим намерением. :с? Я конкретно имею в виду эту строку: $nsNodeDom = $nodeRegistrantDom-›ownerDocument-›importNode(dom_import_simplexml($nsNode), true); - person MEM; 09.08.2010

Поскольку xml загружается из файла, и этот файл объявлен как this ns, может быть, нам следует взять его из этого файла?

Если этот файл является файлом XML, да, вы должны загрузить весь файл, а не только его часть.

Как только пространство имен объявлено, добавить элемент пространства имен очень просто:

<?php
$xml = <<<XML
<epp>
    <domain:create xmlns:domain="urn:someurn" xmlns:ietf="urn:thaturn">
       <domain:name></domain:name>
       <domain:registrant></domain:registrant>
       <domain:contact></domain:contact>
    </domain:create>
</epp>
XML;

$sxml = new SimpleXMLElement($xml);
$sxml->children("domain", true)->create->addChild("newElem", "value", "urn:thaturn");
echo $sxml->saveXML();

дает

<?xml version="1.0"?>
<epp>
    <domain:create xmlns:domain="urn:someurn" xmlns:ietf="urn:thaturn">
       <domain:name/>
       <domain:registrant/>
       <domain:contact/>
    <ietf:newElem>value</ietf:newElem></domain:create>
</epp>
person Artefacto    schedule 09.08.2010
comment
да. Я загружаю XML из файла. Использование addChild — хорошая функция, однако я не могу использовать ее в этом случае, потому что она поместит наш добавленный узел в конец всех братьев и сестер, однако этот узел нужно поместить после ‹domain :регистрант/› узел. Любой другой способ заархивировать это, но не полагаясь на addChild ? К. С уважением, МЕМ - person MEM; 09.08.2010
comment
@MEM См. этот вопрос: stackoverflow.com/questions/3432358/ Вам нужно использовать только DOMDocument::createElementNS вместо DOMDocument::createElement - person Artefacto; 09.08.2010
comment
@Artefacto: Я ожидал, что это будет вопрос одного и того же пользователя ;-) В любом случае, это немного по-другому. В данном случае это не позиционный индекс, а needs to be placed after <domain:registrant/> node. @MEM: Вопрос: всегда ли уже присутствует элемент domain:registrant? Если нет, есть ли запасное правило? - person VolkerK; 09.08.2010
comment
Я могу успешно разместить узел после ‹domain:registrant/›, но я могу сделать это без пространства имен. да. ‹domain:registrant/› всегда будет присутствовать. Большое спасибо. - person MEM; 09.08.2010
comment
@MEM Ну, это тривиальная модификация. Вместо передачи индекса определите позицию этого элемента в массиве $chnodes. - person Artefacto; 09.08.2010
comment
@Artefacto - array_filter и считай, вау. :) Должен признаться, что я запутался. Я могу конечно сделать модификацию и она будет работать как вы сказали, почти наверняка, но дело в том, что мне нужно понять и, несмотря ни на что, я не смог ее получить. Однажды я смогу читать код таким образом, чтобы понимать даже тривиальные изменения. Этот день еще не наступил, извините, что не смог уследить за ним. :( - person MEM; 09.08.2010
comment
@Artefacto Можете ли вы объяснить мне, почему этот $children = $sxml->children("domain", true)->addChild("entry", "value", "urn:someurn"); дает тот же результат? - person giannis christofakis; 28.07.2013